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}