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    private 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        TableCellEditor buttonEditor = new ButtonEditor(new JButton()){
082            // don't want keystrokes to repeatedly fire buttons
083            @Override
084            public void editingStarted(EventObject event) {
085            }
086        };
087        dataTable.setDefaultEditor(JButton.class, buttonEditor);
088
089        // general GUI config
090        setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
091
092        // install items in GUI
093        add(dataScroll);
094
095        // set Viewport preferred size from size of table
096        java.awt.Dimension dataTableSize = dataTable.getPreferredSize();
097        // width is right, but if table is empty, it's not high
098        // enough to reserve much space.
099        dataTableSize.height = Math.max(dataTableSize.height, 400);
100        dataTableSize.width = Math.max(dataTableSize.width, 400);
101        dataScroll.getViewport().setPreferredSize(dataTableSize);
102
103        dataTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
104        JmriMouseListener mouseHeaderListener = new TableHeaderListener();
105        dataTable.getTableHeader().addMouseListener(JmriMouseListener.adapt(mouseHeaderListener));
106    }
107
108    /**
109     * A LccProTable is actually a JPanel 
110     * containing a JTable.  This returns
111     * that contained JTable.
112     */
113    public JTable getTable() {
114        return dataTable;
115    }
116
117    /** 
118     * Provides the DataModel for the contained JTable.
119     */
120    public LccProTableModel getModel() {
121        return dataModel;
122    }
123
124    public final void resetColumnWidths() {
125        Enumeration<TableColumn> en = columnModel.getColumns(false);
126        while (en.hasMoreElements()) {
127            TableColumn tc = en.nextElement();
128            int width = dataModel.getPreferredWidth(tc.getModelIndex());
129            tc.setPreferredWidth(width);
130        }
131        dataTable.sizeColumnsToFit(-1);
132    }
133
134    @Override
135    public void dispose() {
136        if (dataModel != null) {
137            dataModel.dispose();
138        }
139        dataModel = null;
140        dataTable = null;
141        super.dispose();
142    }
143
144    protected void showTableHeaderPopup(JmriMouseEvent e) {
145        JPopupMenu popupMenu = new JPopupMenu();
146        for (int i = 0; i < columnModel.getColumnCount(false); i++) {
147            TableColumn tc = columnModel.getColumnByModelIndex(i);
148            JCheckBoxMenuItem menuItem = new JCheckBoxMenuItem(dataTable.getModel()
149                .getColumnName(i), columnModel.isColumnVisible(tc));
150            menuItem.addActionListener(new HeaderActionListener(tc));
151            popupMenu.add(menuItem);
152
153        }
154        popupMenu.show(e.getComponent(), e.getX(), e.getY());
155    }
156
157    public void setSelectionMode(int selectionMode) {
158        dataTable.setSelectionMode(selectionMode);
159    }
160
161    public int getSelectionMode() {
162        return dataTable.getSelectionModel().getSelectionMode();
163    }
164
165    private class HeaderActionListener implements ActionListener {
166
167        TableColumn tc;
168
169        HeaderActionListener(TableColumn tc) {
170            this.tc = tc;
171        }
172
173        @Override
174        public void actionPerformed(ActionEvent e) {
175            JCheckBoxMenuItem check = (JCheckBoxMenuItem) e.getSource();
176            //Do not allow the last column to be hidden
177            if (!check.isSelected() && columnModel.getColumnCount(true) == 1) {
178                return;
179            }
180            columnModel.setColumnVisible(tc, check.isSelected());
181        }
182    }
183
184    private class TableHeaderListener extends JmriMouseAdapter {
185
186        @Override
187        public void mousePressed(JmriMouseEvent e) {
188            if (e.isPopupTrigger()) {
189                showTableHeaderPopup(e);
190            }
191        }
192
193        @Override
194        public void mouseReleased(JmriMouseEvent e) {
195            if (e.isPopupTrigger()) {
196                showTableHeaderPopup(e);
197            }
198        }
199
200        @Override
201        public void mouseClicked(JmriMouseEvent e) {
202            if (e.isPopupTrigger()) {
203                showTableHeaderPopup(e);
204            }
205        }
206    }
207
208    // private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LccProTable.class);
209
210}