001package jmri.jmrix.openlcb.swing.lccpro;
002
003import java.awt.event.ActionEvent;
004import java.awt.event.ActionListener;
005import java.util.ArrayList;
006import java.util.Enumeration;
007import java.util.EventObject;
008import java.util.List;
009
010import javax.swing.*;
011import javax.swing.table.*;
012
013import jmri.InstanceManager;
014
015import jmri.jmrix.can.CanSystemConnectionMemo;
016
017import jmri.util.gui.GuiLafPreferencesManager;
018import jmri.util.swing.JmriPanel;
019import jmri.util.swing.JmriMouseAdapter;
020import jmri.util.swing.JmriMouseEvent;
021import jmri.util.swing.JmriMouseListener;
022import jmri.util.swing.XTableColumnModel;
023import jmri.util.table.*;
024
025/**
026 * Provide a table of LCC node entries as a JmriJPanel.
027 * <p>
028 * Despite the name, this is-a JPanel, not a JTable.  You
029 * access the contained JTable with the {@link #getTable()} method.
030 *
031 * @author Bob Jacobsen Copyright (C) 2003, 2010, 2024
032 * @author Randall Wood Copyright (C) 2013
033 */
034public class LccProTable extends JmriPanel {
035
036    private LccProTableModel dataModel;
037    TableRowSorter<LccProTableModel> sorter;
038    private JTable dataTable;
039    private JScrollPane dataScroll;
040    private final XTableColumnModel columnModel = new XTableColumnModel();
041
042    public LccProTable(CanSystemConnectionMemo memo) {
043        super();
044        dataModel = new LccProTableModel(memo) {
045            @Override
046            public void forceFocus() {
047                requestFocus();
048            }
049        };
050        
051        sorter = new TableRowSorter<>(dataModel);
052        dataTable = new JTable(dataModel);
053        dataTable.setRowSorter(sorter);
054        dataScroll = new JScrollPane(dataTable);
055        dataTable.setRowHeight(InstanceManager.getDefault(GuiLafPreferencesManager.class).getFontSize() + 4);
056
057        sorter.setComparator(LccProTableModel.IDCOL, new jmri.util.AlphanumComparator());
058
059        // set initial sort
060        List<RowSorter.SortKey> sortKeys = new ArrayList<>();
061        sortKeys.add(new RowSorter.SortKey(LccProTableModel.NAMECOL, SortOrder.ASCENDING));
062        sorter.setSortKeys(sortKeys);
063
064        // allow reordering of the columns
065        dataTable.getTableHeader().setReorderingAllowed(true);
066
067        // have to shut off autoResizeMode to get horizontal scroll to work (JavaSwing p 541)
068        dataTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
069
070        dataTable.setColumnModel(columnModel);
071        dataTable.createDefaultColumnsFromModel();
072        dataTable.setAutoCreateColumnsFromModel(false);
073
074        // resize columns as requested
075        resetColumnWidths();
076
077        // Button rendering
078        ButtonRenderer buttonRenderer = new ButtonRenderer();
079        columnModel.getColumn(LccProTableModel.CONFIGURECOL).setCellRenderer(buttonRenderer);
080        columnModel.getColumn(LccProTableModel.UPGRADECOL).setCellRenderer(buttonRenderer);
081        columnModel.getColumn(LccProTableModel.BACKUPCOL).setCellRenderer(buttonRenderer);
082        TableCellEditor buttonEditor = new ButtonEditor(new JButton()){
083            // don't want keystrokes to repeatedly fire buttons
084            @Override
085            public void editingStarted(EventObject event) {
086            }
087        };
088        dataTable.setDefaultEditor(JButton.class, buttonEditor);
089
090        // general GUI config
091        setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
092
093        // install items in GUI
094        add(dataScroll);
095
096        // set Viewport preferred size from size of table
097        java.awt.Dimension dataTableSize = dataTable.getPreferredSize();
098        // width is right, but if table is empty, it's not high
099        // enough to reserve much space.
100        dataTableSize.height = Math.max(dataTableSize.height, 400);
101        dataTableSize.width = Math.max(dataTableSize.width, 400);
102        dataScroll.getViewport().setPreferredSize(dataTableSize);
103
104        dataTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
105        JmriMouseListener mouseHeaderListener = new TableHeaderListener();
106        dataTable.getTableHeader().addMouseListener(JmriMouseListener.adapt(mouseHeaderListener));
107    }
108
109    /**
110     * A LccProTable is actually a JPanel 
111     * containing a JTable.  This returns
112     * that contained JTable.
113     */
114    public JTable getTable() {
115        return dataTable;
116    }
117
118    /** 
119     * Provides the DataModel for the contained JTable.
120     */
121    public LccProTableModel getModel() {
122        return dataModel;
123    }
124
125    public final void resetColumnWidths() {
126        Enumeration<TableColumn> en = columnModel.getColumns(false);
127        while (en.hasMoreElements()) {
128            TableColumn tc = en.nextElement();
129            int width = dataModel.getPreferredWidth(tc.getModelIndex());
130            tc.setPreferredWidth(width);
131        }
132        dataTable.sizeColumnsToFit(-1);
133    }
134
135    @Override
136    public void dispose() {
137        if (dataModel != null) {
138            dataModel.dispose();
139        }
140        dataModel = null;
141        dataTable = null;
142        super.dispose();
143    }
144
145    protected void showTableHeaderPopup(JmriMouseEvent e) {
146        JPopupMenu popupMenu = new JPopupMenu();
147        for (int i = 0; i < columnModel.getColumnCount(false); i++) {
148            TableColumn tc = columnModel.getColumnByModelIndex(i);
149            JCheckBoxMenuItem menuItem = new JCheckBoxMenuItem(dataTable.getModel()
150                .getColumnName(i), columnModel.isColumnVisible(tc));
151            menuItem.addActionListener(new HeaderActionListener(tc));
152            popupMenu.add(menuItem);
153
154        }
155        popupMenu.show(e.getComponent(), e.getX(), e.getY());
156    }
157
158    public void setSelectionMode(int selectionMode) {
159        dataTable.setSelectionMode(selectionMode);
160    }
161
162    public int getSelectionMode() {
163        return dataTable.getSelectionModel().getSelectionMode();
164    }
165
166    private class HeaderActionListener implements ActionListener {
167
168        TableColumn tc;
169
170        HeaderActionListener(TableColumn tc) {
171            this.tc = tc;
172        }
173
174        @Override
175        public void actionPerformed(ActionEvent e) {
176            JCheckBoxMenuItem check = (JCheckBoxMenuItem) e.getSource();
177            //Do not allow the last column to be hidden
178            if (!check.isSelected() && columnModel.getColumnCount(true) == 1) {
179                return;
180            }
181            columnModel.setColumnVisible(tc, check.isSelected());
182        }
183    }
184
185    private class TableHeaderListener extends JmriMouseAdapter {
186
187        @Override
188        public void mousePressed(JmriMouseEvent e) {
189            if (e.isPopupTrigger()) {
190                showTableHeaderPopup(e);
191            }
192        }
193
194        @Override
195        public void mouseReleased(JmriMouseEvent e) {
196            if (e.isPopupTrigger()) {
197                showTableHeaderPopup(e);
198            }
199        }
200
201        @Override
202        public void mouseClicked(JmriMouseEvent e) {
203            if (e.isPopupTrigger()) {
204                showTableHeaderPopup(e);
205            }
206        }
207    }
208
209    // private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LccProTable.class);
210
211}