001package jmri.jmrix.loconet.slotmon; 002 003import java.awt.Component; 004import java.awt.Dimension; 005import java.awt.FontMetrics; 006import java.awt.event.ActionEvent; 007import java.awt.event.ActionListener; 008import java.awt.event.ItemListener; 009import java.util.ArrayList; 010import java.util.List; 011import javax.swing.*; 012import javax.swing.border.TitledBorder; 013import javax.swing.event.ListSelectionEvent; 014import javax.swing.event.ListSelectionListener; 015import javax.swing.table.DefaultTableCellRenderer; 016import javax.swing.table.TableCellEditor; 017import javax.swing.table.TableColumn; 018import javax.swing.table.TableColumnModel; 019import javax.swing.table.TableRowSorter; 020 021import jmri.InstanceManager; 022import jmri.UserPreferencesManager; 023import jmri.jmrix.loconet.LnConstants; 024import jmri.jmrix.loconet.LocoNetSlot; 025import jmri.jmrix.loconet.SlotListener; 026import jmri.jmrix.loconet.SlotMapEntry.SlotType; 027import jmri.jmrix.loconet.SlotManager; 028import jmri.swing.JmriJTablePersistenceManager; 029import jmri.util.MenuScroller; 030import jmri.util.swing.JmriMouseAdapter; 031import jmri.util.swing.JmriMouseEvent; 032import jmri.util.swing.JmriMouseListener; 033import jmri.util.swing.WrapLayout; 034import jmri.util.swing.XTableColumnModel; 035import jmri.util.table.*; 036 037/** 038 * Frame providing a command station slot manager. 039 * <p> 040 * Slots 102 through 127 are normally not used for loco control, so are shown 041 * separately. 042 * 043 * @author Bob Jacobsen Copyright (C) 2001 044 */ 045public class SlotMonPane extends jmri.jmrix.loconet.swing.LnPanel implements SlotListener { 046 047 /** 048 * Controls whether not-in-use slots are shown 049 */ 050 protected final JCheckBox showUnusedCheckBox = new JCheckBox(); 051 /** 052 * Controls whether system slots (0, 121-127) are shown 053 */ 054 protected final JCheckBox showSystemCheckBox = new JCheckBox(); 055 056 private JLabel dcsCSLabel = new JLabel(Bundle.getMessage("SlotMonCSLabel")); 057 private JTextField dcsType = new JTextField(); 058 private JLabel dcsSlotsLabel = new JLabel(Bundle.getMessage("SlotMonTotalSlots")); 059 private JTextField dcsSlots = new JTextField(); 060 private JTextField summaryInUse = new JTextField(); 061 private JTextField summaryCommon = new JTextField(); 062 private JTextField summaryTotalActive = new JTextField(); 063 private TitledBorder summaryBorder = BorderFactory.createTitledBorder(Bundle.getMessage("SlotMonSlotsSummary")); 064 private JLabel summaryInUseLabel = new JLabel(Bundle.getMessage("SlotMonSlotsSummaryInUse") + ":"); 065 private JLabel summaryCommonLabel = new JLabel(Bundle.getMessage("SlotMonSlotsSummaryCommon") + ":"); 066 private JLabel summaryTotalActiveLabel = new JLabel(Bundle.getMessage("SlotMonSlotsSummaryActive") + ":"); 067 068 private final JButton estopAllButton = new JButton(Bundle.getMessage("ButtonSlotMonEStopAll")); 069 070 private JLabel inUseCount = new JLabel(" "); 071 072 //Added by Jeffrey Machacek 2013 073 private final JButton clearAllButton = new JButton(Bundle.getMessage("ButtonSlotMonClearAll")); 074 private final JButton refreshAllButton = new JButton(Bundle.getMessage("ButtonSlotRefresh")); 075 076 private JPanel topPanel; // the panel across the top that holds buttons 077 private JPanel summaryPanel; // single row for assigned totals 078 079 private SlotMonDataModel slotModel; 080 private JTable slotTable; 081 private JScrollPane slotScroll; 082 private transient TableRowSorter<SlotMonDataModel> sorter; 083 private boolean hideColumnsF9toF28 = false; 084 085 // Options menu 086 private JCheckBoxMenuItem optionHideTopPanel = new JCheckBoxMenuItem(Bundle.getMessage("SlotMonHideTopPanel")); 087 private JCheckBoxMenuItem optionHideSummaryPanel = new JCheckBoxMenuItem(Bundle.getMessage("SlotMonHideSummaryPanel")); 088 089 // filter menu 090 private JCheckBoxMenuItem filterShowIdle = new JCheckBoxMenuItem(Bundle.getMessage("SlotMonShowIdle")); 091 private JCheckBoxMenuItem filterShowUnUsed = new JCheckBoxMenuItem(Bundle.getMessage("TextSlotMonShowUnused")); 092 private JCheckBoxMenuItem filterShowSystem = new JCheckBoxMenuItem(Bundle.getMessage("TextSlotMonShowSystem")); 093 094 // Actions menu 095 private JMenuItem actionRefreshAll = new JMenuItem(Bundle.getMessage("ButtonSlotRefresh")); 096 private JMenuItem actionEStopAll = new JMenuItem(Bundle.getMessage("ButtonSlotMonEStopAll")); 097 private JMenuItem actionSaveFiltersAndOptions = new JMenuItem(Bundle.getMessage("SlotMonSaveFiltersAndOptions")); 098 private JMenuItem actionResetColumnsLayout = new JMenuItem(Bundle.getMessage("SlotMonResetColumnsLayout")); 099 100 // popup menu 101 private JPopupMenu popUp = new JPopupMenu(); 102 private JMenuItem popUpRefresh = new JMenuItem("Refresh"); 103 private int selectedRow = -1; 104 105 private final String slotMonHideTopPanel = this.getClass().getName() + "SlotMonHideTopPanel"; // NOI18N 106 private final String slotMonHideSummaryPanel = this.getClass().getName() + "SlotMonHideSummaryPanel"; // NOI18N 107 private final String slotMonShowIdle = this.getClass().getName() + "SlotMonShowIdle"; // NOI18N 108 private final String slotMonShowUnused = this.getClass().getName() + "SlotMonShowUnused"; // NOI18N 109 private final String slotMonShowSystem = this.getClass().getName() + "SlotMonShowSystem"; // NOI18N 110 111 private void restorePreferences() { 112 UserPreferencesManager pm = InstanceManager.getDefault(UserPreferencesManager.class); 113 optionHideTopPanel.setSelected(pm.getSimplePreferenceState(slotMonHideTopPanel)); 114 optionHideSummaryPanel.setSelected(pm.getSimplePreferenceState(slotMonHideSummaryPanel)); 115 filterShowIdle.setSelected(pm.getSimplePreferenceState(slotMonShowIdle)); 116 filterShowUnUsed.setSelected(pm.getSimplePreferenceState(slotMonShowUnused)); 117 filterShowSystem.setSelected(pm.getSimplePreferenceState(slotMonShowSystem)); 118 } 119 120 private void resetColumnLayout() { 121 //JmriJTablePersistenceManager has issues. 122 InstanceManager.getOptionalDefault(JmriJTablePersistenceManager.class).ifPresent((tpm) -> { 123 try { 124 tpm.stopPersisting(slotTable); 125 tpm.clearState(slotTable); 126 slotTable.getTableHeader().removeMouseListener(JmriMouseListener.adapt(mouseHeaderListener)); 127 XTableColumnModel tcm = (XTableColumnModel) slotTable.getColumnModel(); 128 129 for (int i = 0; i < tcm.getColumnCount(false); i++) { 130 TableColumn tc = tcm.getColumnByModelIndex(i); 131 tcm.setColumnVisible(tc, true); 132 } 133 // NB do not use the XTable version 134 TableColumnModel slotColModel = slotTable.getColumnModel(); 135 int colCount = slotColModel.getColumnCount(); 136 // Sort columns based on their original model index 137 // Primative but works. 138 boolean swapped = true; 139 while (swapped) { 140 swapped = false; 141 for (int i = 0; i < colCount; i++) { 142 for (int j = i + 1; j < colCount; j++) { 143 if (slotColModel.getColumn(i).getModelIndex() > slotColModel.getColumn(j).getModelIndex()) { 144 slotColModel.moveColumn(i, j); 145 swapped = true; 146 } 147 } 148 } 149 } 150 tpm.persist(slotTable,true); 151 setColumnsF9toF28(hideColumnsF9toF28); 152 addMouseListenerToHeader(slotTable); 153 } catch (IllegalArgumentException Ex) { 154 log.warn("SlotMon Can only save layout changes for second and subsequent invocations"); 155 } 156 }); 157 } 158 159 private void savePreferences() { 160 UserPreferencesManager pm = InstanceManager.getDefault(UserPreferencesManager.class); 161 pm.setSimplePreferenceState(slotMonHideTopPanel, optionHideTopPanel.isSelected()); 162 pm.setSimplePreferenceState(slotMonHideSummaryPanel, optionHideSummaryPanel.isSelected()); 163 pm.setSimplePreferenceState(slotMonShowIdle, filterShowIdle.isSelected()); 164 pm.setSimplePreferenceState(slotMonShowUnused, filterShowUnUsed.isSelected()); 165 pm.setSimplePreferenceState(slotMonShowSystem, filterShowSystem.isSelected()); 166 } 167 168 public SlotMonPane() { 169 super(); 170 } 171 172 @Override 173 public boolean isMultipleInstances() { 174 return false; 175 } 176 177 private boolean runningFilterSystem = false; 178 private boolean runningFilterUnUsed = false; 179 180 private SlotManager slotManager; 181 182 @Override 183 public void initComponents(jmri.jmrix.loconet.LocoNetSystemConnectionMemo memo) { 184 super.initComponents(memo); 185 slotManager = memo.getSlotManager(); // save for later. 186 hideColumnsF9toF28 = (memo.getSlotManager().getLoconetProtocol() != LnConstants.LOCONETPROTOCOL_TWO); 187 188 slotModel = new SlotMonDataModel(slotManager.getNumSlots(), memo); 189 slotTable = new JTable(slotModel); 190 slotTable.setColumnModel(new XTableColumnModel()); 191 slotTable.createDefaultColumnsFromModel(); 192 //XTableColumnModel SlotMonDataModel = (XTableColumnModel)slotTable.getColumnModel(); 193 194 slotTable.setName(this.getTitle()); 195 196 sorter = new TableRowSorter<>(slotModel); 197 slotTable.setRowSorter(sorter); 198 slotScroll = new JScrollPane(slotTable); 199 200 // configure items for GUI 201 showUnusedCheckBox.setText(Bundle.getMessage("TextSlotMonShowUnused")); 202 showUnusedCheckBox.setVisible(true); 203 showUnusedCheckBox.setSelected(false); 204 showUnusedCheckBox.setToolTipText(Bundle.getMessage("TooltipSlotMonShowUnused")); 205 206 showSystemCheckBox.setText(Bundle.getMessage("TextSlotMonShowSystem")); 207 showSystemCheckBox.setVisible(true); 208 showSystemCheckBox.setSelected(false); 209 showSystemCheckBox.setToolTipText(Bundle.getMessage("TooltipSlotMonShowSystem")); 210 211 // allow reordering of the columns 212 slotTable.getTableHeader().setReorderingAllowed(true); 213 214 // shut off autoResizeMode to get horizontal scroll to work (JavaSwing p 541) 215 slotTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); 216 217 // resize columns as requested 218 for (int i = 0; i < slotTable.getColumnCount(); i++) { 219 int width = slotModel.getPreferredWidth(i); 220 slotTable.getColumnModel().getColumn(i).setPreferredWidth(width); 221 } 222 slotTable.sizeColumnsToFit(-1); 223 224 // install a button renderer & editor in the "DISP" column for freeing a slot 225 setColumnToHoldButton(slotTable, slotTable.convertColumnIndexToView(SlotMonDataModel.ColumnNumber.DISPCOLUMN.ordinal())); 226 227 // install a button renderer & editor in the "ESTOP" column for stopping a loco 228 setColumnToHoldEStopButton(slotTable, slotTable.convertColumnIndexToView(SlotMonDataModel.ColumnNumber.ESTOPCOLUMN.ordinal())); 229 230 // Install a numeric format for ConsistAddress 231 slotTable.getColumnModel().getColumn(SlotMonDataModel.ColumnNumber.CONSISTADDRESS.ordinal()).setCellRenderer(new TableRendererToBlankToNA()); 232 slotTable.getColumnModel().getColumn(slotTable.convertColumnIndexToView(SlotMonDataModel.ColumnNumber.JMRITHROTTLECOUNT.ordinal())).setCellRenderer(new TableRendererToBlankToNA()); 233 234 InstanceManager.getOptionalDefault(JmriJTablePersistenceManager.class).ifPresent((tpm) -> { 235 try { 236 tpm.persist(slotTable, true); 237 } catch (IllegalArgumentException Ex) { 238 log.warn("SlotMon Can only save layout changes for second and subsequent invocations"); 239 } 240 }); 241 242 setColumnsF9toF28(hideColumnsF9toF28); 243 244 ActionListener refreshListener = e -> { 245 slotModel.refreshSlots(); 246 }; 247 // add listener object so checkboxes functio 248 refreshAllButton.addActionListener(refreshListener); 249 actionRefreshAll.addActionListener(refreshListener); 250 251 ItemListener filterShowSystemListen = e -> { 252 if (runningFilterSystem) { 253 return; 254 } 255 runningFilterSystem = true; 256 boolean state; 257 if (e.getSource() instanceof JCheckBox) { 258 state = ((JCheckBox) e.getSource()).isSelected(); 259 } else { 260 state = ((JCheckBoxMenuItem) e.getSource()).isSelected(); 261 } 262 filterShowSystem.setSelected(state); 263 showSystemCheckBox.setSelected(state); 264 runningFilterSystem = false; 265 filter(); 266 } ; 267 showSystemCheckBox.addItemListener(filterShowSystemListen); 268 filterShowSystem.addItemListener(filterShowSystemListen); 269 270 ItemListener filterShowUnUsedListen = e -> { 271 if (runningFilterUnUsed) { 272 return; 273 } 274 runningFilterUnUsed = true; 275 boolean state; 276 if (e.getSource() instanceof JCheckBox) { 277 state = ((JCheckBox) e.getSource()).isSelected(); 278 } else { 279 state = ((JCheckBoxMenuItem) e.getSource()).isSelected(); 280 } 281 filterShowUnUsed.setSelected(state); 282 showUnusedCheckBox.setSelected(state); 283 runningFilterUnUsed = false; 284 filter(); 285 }; 286 filterShowUnUsed.addItemListener(filterShowUnUsedListen); 287 showUnusedCheckBox.addItemListener(filterShowUnUsedListen); 288 filterShowIdle.addActionListener((ActionEvent e) -> { 289 filter(); 290 }); 291 292 // add listener to menu items 293 optionHideTopPanel.addActionListener((ActionEvent e) -> { 294 topPanel.setVisible(!optionHideTopPanel.getState()); 295 }); 296 297 optionHideSummaryPanel.addActionListener((ActionEvent e) -> { 298 summaryPanel.setVisible(!optionHideSummaryPanel.getState()); 299 }); 300 301 estopAllButton.addActionListener((ActionEvent e) -> { 302 slotModel.estopAll(); 303 }); 304 actionEStopAll.addActionListener((ActionEvent e) -> { 305 slotModel.estopAll(); 306 }); 307 actionSaveFiltersAndOptions.addActionListener((ActionEvent e) -> { 308 savePreferences(); 309 }); 310 actionResetColumnsLayout.addActionListener((ActionEvent e) -> { 311 resetColumnLayout(); 312 }); 313 314 //Jeffrey 6/29/2013 315 clearAllButton.addActionListener((ActionEvent e) -> { 316 slotModel.clearAllSlots(); 317 }); 318 319 // adjust model to default settings 320 filter(); 321 322 // This listener is needed when there are a large number of slots being updated 323 // 324 slotTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() { 325 @Override 326 public void valueChanged(ListSelectionEvent e) { 327 if (slotTable.getSelectedRow() != -1) { // Ensure a row is actually selected 328 selectedRow = slotTable.getSelectedRow(); 329 Object value = slotTable.getValueAt(selectedRow, 0); 330 log.debug("Selected Row:[{}] Value[{}]", selectedRow, value); 331 } 332 } 333 }); 334 popUp.add(popUpRefresh); 335 popUpRefresh.addActionListener((ActionEvent e) -> { 336 int row = slotTable.getSelectedRow(); 337 if (row != -1) { 338 memo.getSlotManager().sendReadSlot((int)slotTable.getValueAt(row, 0)); 339 } else { 340 String response = jmri.util.swing.JmriJOptionPane.showInputDialog(this, 341 "Enter Slot", Integer.toString(selectedRow) ); 342 try { 343 int slotNo = Integer.parseInt(response) ; 344 memo.getSlotManager().sendReadSlot(slotNo); 345 } catch (NumberFormatException ex) { 346 log.error("Non Number[{}], ignored",response); 347 } 348 } 349 }); 350 slotTable.setComponentPopupMenu(popUp); 351 352 // general GUI config 353 setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); 354 355 // install items in GUI 356 topPanel = new JPanel(); 357 topPanel.setLayout(new WrapLayout()); 358 359 topPanel.add(dcsCSLabel); 360 dcsType.setEditable(false); 361 topPanel.add(dcsType); 362 topPanel.add(dcsSlotsLabel); 363 dcsSlots.setEditable(false); 364 topPanel.add(dcsSlots); 365 showHideSlot250Data(false); 366 topPanel.add(refreshAllButton); 367 topPanel.add(showUnusedCheckBox); 368 topPanel.add(showSystemCheckBox); 369 topPanel.add(estopAllButton); 370 topPanel.add(clearAllButton); 371 topPanel.add(clearAllButton); 372 topPanel.add(new JLabel(Bundle.getMessage("SlotMonUnUseCountLabel"))); 373 topPanel.add(inUseCount); 374 375 summaryPanel = new JPanel(); 376 summaryPanel.setBorder(summaryBorder); 377 FontMetrics summaryMaxHeight = summaryPanel.getFontMetrics(summaryPanel.getFont()); 378 summaryPanel.setMaximumSize(new Dimension(Integer.MAX_VALUE, summaryMaxHeight.getHeight()*2)); 379 summaryPanel.add(summaryTotalActiveLabel); 380 summaryPanel.add(summaryTotalActive); 381 summaryTotalActive.setEditable(false); 382 summaryPanel.add(summaryInUseLabel); 383 summaryPanel.add(summaryInUse); 384 summaryInUse.setEditable(false); 385 summaryPanel.add(summaryCommonLabel); 386 summaryPanel.add(summaryCommon); 387 summaryCommon.setEditable(false); 388 389 add(summaryPanel); 390 add(topPanel); 391 add(slotScroll); 392 393 restorePreferences(); 394 395 addMouseListenerToHeader(slotTable); 396 397 398 memo.getSlotManager().addSlotListener(this); 399 400 // set top panel size 401 if (topPanel.getMaximumSize().height > 0 && topPanel.getMaximumSize().width > 0) { 402 topPanel.setMaximumSize(topPanel.getPreferredSize()); 403 } 404 topPanel.setVisible(!optionHideTopPanel.getState()); 405 summaryPanel.setVisible(!optionHideSummaryPanel.getState()); 406 } 407 408 private void setColumnsF9toF28(boolean hideColumnsF9toF28) { 409 // Hide functions F9 thru F28 if not used. 410 if (hideColumnsF9toF28) { 411 XTableColumnModel tcm = (XTableColumnModel) slotTable.getColumnModel(); 412 for (int ixCol = SlotMonDataModel.ColumnNumber.F9COLUMN.ordinal() ; 413 ixCol <= SlotMonDataModel.ColumnNumber.F28COLUMN.ordinal() ; 414 ixCol ++ ) { 415 TableColumn tc = tcm.getColumnByModelIndex(ixCol); 416 tcm.setColumnVisible(tc, false); 417 } 418 } 419 } 420 421 void setColumnToHoldButton(JTable slotTable, int column) { 422 TableColumnModel tcm = slotTable.getColumnModel(); 423 // install the button renderers & editors in this column 424 ButtonRenderer buttonRenderer = new ButtonRenderer(); 425 tcm.getColumn(column).setCellRenderer(buttonRenderer); 426 // ensure the table rows, columns have enough room for buttons 427 TableCellEditor buttonEditor = new ButtonEditor(new JButton()); 428 slotTable.setDefaultEditor(JButton.class, buttonEditor); 429 slotTable.setRowHeight(new JButton(" " + slotModel.getValueAt(1, column)).getPreferredSize().height); 430 slotTable.getColumnModel().getColumn(column) 431 .setPreferredWidth(new JButton(" " + slotModel.getValueAt(1, column)).getPreferredSize().width); 432 } 433 434 static class TableRendererToBlankToNA extends DefaultTableCellRenderer { 435 @Override 436 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, 437 int row, int column) { 438 if ((Integer) value == 0) { 439 setText(""); 440 } else if ((Integer) value == -1) { 441 setText(Bundle.getMessage("SlotDataNotApplicable")); 442 } else { 443 setText(Integer.toString((Integer) value)); 444 } 445 return this; 446 } 447 } 448 449 void setColumnToHoldEStopButton(JTable slotTable, int column) { 450 TableColumnModel tcm = slotTable.getColumnModel(); 451 // install the button renderers & editors in this column 452 ButtonRenderer buttonRenderer = new ButtonRenderer(); 453 tcm.getColumn(column).setCellRenderer(buttonRenderer); 454 // ensure the table rows, columns have enough room for buttons 455 TableCellEditor buttonEditor = new ButtonEditor(new JButton()); 456 slotTable.setDefaultEditor(JButton.class, buttonEditor); 457 slotTable.setRowHeight(new JButton(" " + slotModel.getValueAt(1, column)).getPreferredSize().height); 458 slotTable.getColumnModel().getColumn(column) 459 .setPreferredWidth(new JButton(" " + slotModel.getValueAt(1, column)).getPreferredSize().width); 460 } 461 462 @Override 463 public String getHelpTarget() { 464 return "package.jmri.jmrix.loconet.slotmon.SlotMonFrame"; // NOI18N 465 } 466 467 @Override 468 public String getTitle() { 469 return getTitle(Bundle.getMessage("MenuItemSlotMonitor")); 470 } 471 472 @Override 473 public void dispose() { 474 InstanceManager.getOptionalDefault(JmriJTablePersistenceManager.class).ifPresent( tpm -> 475 tpm.stopPersisting(slotTable) ); 476 savePreferences(); 477 slotModel.dispose(); 478 slotModel = null; 479 slotTable = null; 480 slotScroll = null; 481 super.dispose(); 482 } 483 484 485 private void filter() { 486 RowFilter<SlotMonDataModel, Integer> rf = new RowFilter<SlotMonDataModel, Integer>() { 487 @Override 488 public boolean include(RowFilter.Entry<? extends SlotMonDataModel, ? extends Integer> entry) { 489 // default filter is IN-USE and regular systems slot 490 // the default is whatever the person last closed it with 491 jmri.jmrix.loconet.LocoNetSlot slot = entry.getModel().getSlot(entry.getIdentifier()); 492 boolean include = (slot.slotStatus() != LnConstants.LOCO_FREE 493 && slot.slotStatus() != LnConstants.LOCO_IDLE) 494 && slot.getSlotType() == SlotType.LOCO; 495 if (slot.getSlotType() == SlotType.UNKNOWN) { 496 return false; // dont ever show unknown 497 } 498 if (!include && filterShowIdle.isSelected() 499 && slot.slotStatus() == LnConstants.LOCO_IDLE 500 && !slot.isSystemSlot()) { 501 include = true; 502 } 503 if (!include && showUnusedCheckBox.isSelected() && !slot.isSystemSlot()) { 504 include = true; 505 } 506 if (!include && showSystemCheckBox.isSelected() && slot.isSystemSlot()) { 507 include = true; 508 } 509 return include; 510 } 511 }; 512 sorter.setRowFilter(rf); 513 } 514 515 @Override 516 public List<JMenu> getMenus() { 517 List<JMenu> menuList = new ArrayList<>(); 518 menuList.add(getFileMenu()); 519 menuList.add(getOptionsMenu()); 520 menuList.add(getFiltersMenu()); 521 menuList.add(getActionsMenu()); 522 return menuList; 523 } 524 525 private JMenu getOptionsMenu() { 526 JMenu optionsMenu = new JMenu(Bundle.getMessage("MenuOptions")); 527 optionsMenu.add(optionHideTopPanel); 528 optionsMenu.add(optionHideSummaryPanel); 529 return optionsMenu; 530 } 531 532 private JMenu getFiltersMenu() { 533 JMenu filtersMenu = new JMenu(Bundle.getMessage("MenuFilters")); 534 filtersMenu.add(filterShowIdle); 535 filtersMenu.add(filterShowSystem); 536 filtersMenu.add(filterShowUnUsed); 537 return filtersMenu; 538 } 539 540 private JMenu getActionsMenu() { 541 JMenu actionsMenu = new JMenu(Bundle.getMessage("MenuActions")); 542 actionsMenu.add(actionRefreshAll); 543 actionsMenu.add(actionEStopAll); 544 actionsMenu.add(actionResetColumnsLayout); 545 actionsMenu.add(actionSaveFiltersAndOptions); 546 return actionsMenu; 547 } 548 549 private JMenu getFileMenu(){ 550 JMenu fileMenu = new JMenu(Bundle.getMessage("MenuFile")); // NOI18N 551 fileMenu.add(new JTableToCsvAction((Bundle.getMessage("ExportCsvAll")), 552 null, slotModel, "Slot_Monitor_All.csv", new int[]{ 553 SlotMonDataModel.ColumnNumber.ESTOPCOLUMN.ordinal()})); // NOI18N 554 fileMenu.add(new JTableToCsvAction((Bundle.getMessage("ExportCsvView")), 555 slotTable, slotModel, "Slot_Monitor_View.csv", new int[]{ 556 SlotMonDataModel.ColumnNumber.ESTOPCOLUMN.ordinal()})); // NOI18N 557 return fileMenu; 558 } 559 560 // update count 561 int inUseSlotCount = 0; 562 int idleSlotCount = 0; 563 int freeSlotCount = 0; 564 int commonSlotCount = 0; 565 int otherSlotCount = 0; 566 567 // methods to communicate with SlotManager 568 @Override 569 public synchronized void notifyChangedSlot(LocoNetSlot s) { 570 // update model from this slot 571 if (s.getSlot() == 250) { 572 if (memo.getSlotManager().getSlot250CSSlots() > 0) { 573 showHideSlot250Data(true); 574 dcsSlots.setText(Integer.toString(memo.getSlotManager().getSlot250CSSlots())); 575 dcsType.setText(memo.getSlotManager().getSlot248CommandStationType()); 576 577 // set scroll size 578 if (topPanel.getMaximumSize().height > 0 && topPanel.getMaximumSize().width > 0) { 579 topPanel.setMaximumSize(topPanel.getPreferredSize()); 580 } 581 topPanel.revalidate(); 582 } 583 } 584 if (s.getSlot() == 127) { 585 StringBuilder tString = new StringBuilder(Bundle.getMessage("SlotMonSlotsSummary")); 586 if (slotManager.getCsOpSw(13) != null ) { 587 if (slotManager.getCsOpSw(14)==SlotManager.CsOpSwValue.CLOSED) { 588 tString.append(" "); 589 tString.append(Bundle.getMessage("SlotMonPurgeingDisabled")); 590 } else if (slotManager.getCsOpSw(13)==SlotManager.CsOpSwValue.CLOSED) { 591 tString.append(" "); 592 tString.append(Bundle.getMessage("SlotMonPurgeingExtended")); 593 } 594 if (slotManager.getCsOpSw(44)==SlotManager.CsOpSwValue.CLOSED) { 595 tString.append(" "); 596 tString.append(Bundle.getMessage("SlotMonSlotNumberNotDefault")); 597 } 598 } 599 summaryBorder.setTitle(tString.toString()); 600 repaint(); 601 } 602 603 // update count 604 inUseSlotCount = 0; 605 idleSlotCount = 0; 606 freeSlotCount = 0; 607 commonSlotCount = 0; 608 otherSlotCount = 0; 609 for (var slot : memo.getSlotManager().getSlots()) { 610 if (!slot.isSystemSlot()) { 611 switch (slot.slotStatus()) { 612 case LnConstants.LOCO_IN_USE: 613 inUseSlotCount++; 614 break; 615 case LnConstants.LOCO_IDLE: 616 idleSlotCount++; 617 break; 618 case LnConstants.LOCO_FREE: 619 freeSlotCount++; 620 break; 621 case LnConstants.LOCO_COMMON: 622 commonSlotCount++; 623 break; 624 default: 625 log.error("Loco Slot[{}] has unknown status.",slot.getSlot()); 626 } 627 } 628 } 629 inUseCount.setText(""+inUseSlotCount); 630 summaryCommon.setText(Integer.toString(commonSlotCount)); 631 summaryInUse.setText(Integer.toString(inUseSlotCount)); 632 summaryTotalActive.setText(Integer.toString(commonSlotCount + inUseSlotCount)); 633 } 634 635 void showHideSlot250Data(boolean b) { 636 dcsCSLabel.setVisible(b); 637 dcsSlots.setVisible(b); 638 dcsSlotsLabel.setVisible(b); 639 dcsType.setVisible(b); 640 // set scroll size 641 if (topPanel.getMaximumSize().height > 0 && topPanel.getMaximumSize().width > 0) { 642 topPanel.setMaximumSize(topPanel.getPreferredSize()); 643 } 644 topPanel.revalidate(); 645 } 646 647 /* 648 * Mouse popup stuff 649 */ 650 651 /** 652 * Process the column header click 653 * @param e the evnt data 654 * @param table the JTable 655 */ 656 protected void showTableHeaderPopup(JmriMouseEvent e, JTable table) { 657 JPopupMenu popupMenu = new JPopupMenu(); 658 XTableColumnModel tcm = (XTableColumnModel) table.getColumnModel(); 659 for (int i = 0; i < tcm.getColumnCount(false); i++) { 660 TableColumn tc = tcm.getColumnByModelIndex(i); 661 String columnName = table.getModel().getColumnName(i); 662 if (columnName != null && !columnName.equals("")) { 663 JCheckBoxMenuItem menuItem = new JCheckBoxMenuItem(table.getModel().getColumnName(i), tcm.isColumnVisible(tc)); 664 menuItem.addActionListener(new HeaderActionListener(tc, tcm)); 665 popupMenu.add(menuItem); 666 } 667 } 668 MenuScroller.setScrollerFor(popupMenu,10,200,0,0); 669 popupMenu.show(e.getComponent(), e.getX(), e.getY()); 670 671 } 672 673 JmriMouseListener mouseHeaderListener; 674 /** 675 * Adds the column header pop listener to a JTable using XTableColumnModel 676 * @param table The JTable effected. 677 */ 678 protected void addMouseListenerToHeader(JTable table) { 679 mouseHeaderListener = new TableHeaderListener(table); 680 table.getTableHeader().addMouseListener(JmriMouseListener.adapt(mouseHeaderListener)); 681 } 682 683 protected static class HeaderActionListener implements ActionListener { 684 685 TableColumn tc; 686 XTableColumnModel tcm; 687 688 HeaderActionListener(TableColumn tc, XTableColumnModel tcm) { 689 this.tc = tc; 690 this.tcm = tcm; 691 } 692 693 @Override 694 public void actionPerformed(ActionEvent e) { 695 JCheckBoxMenuItem check = (JCheckBoxMenuItem) e.getSource(); 696 //Do not allow the last column to be hidden 697 if (!check.isSelected() && tcm.getColumnCount(true) == 1) { 698 return; 699 } 700 tcm.setColumnVisible(tc, check.isSelected()); 701 } 702 } 703 704 /** 705 * Class to support Columnheader popup menu on XTableColum model. 706 */ 707 class TableHeaderListener extends JmriMouseAdapter { 708 709 JTable table; 710 711 TableHeaderListener(JTable tbl) { 712 super(); 713 table = tbl; 714 } 715 716 /** 717 * {@inheritDoc} 718 */ 719 @Override 720 public void mousePressed(JmriMouseEvent e) { 721 if (e.isPopupTrigger()) { 722 showTableHeaderPopup(e, table); 723 } 724 } 725 726 /** 727 * {@inheritDoc} 728 */ 729 @Override 730 public void mouseReleased(JmriMouseEvent e) { 731 if (e.isPopupTrigger()) { 732 showTableHeaderPopup(e, table); 733 } 734 } 735 736 /** 737 * {@inheritDoc} 738 */ 739 @Override 740 public void mouseClicked(JmriMouseEvent e) { 741 if (e.isPopupTrigger()) { 742 showTableHeaderPopup(e, table); 743 } 744 } 745 } 746 747 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(SlotMonPane.class); 748 749}