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}