001package jmri.jmrix.can.cbus.swing.eventtable; 002 003import java.awt.Component; 004import java.awt.Dimension; 005import java.awt.event.ActionEvent; 006import java.text.DateFormat; 007import java.text.SimpleDateFormat; 008import java.util.Arrays; 009import java.util.regex.PatternSyntaxException; 010import javax.swing.*; 011import javax.swing.event.DocumentEvent; 012import javax.swing.event.DocumentListener; 013import javax.swing.event.TableModelEvent; 014import javax.swing.event.TableModelListener; 015import javax.swing.table.TableCellRenderer; 016import javax.swing.table.TableColumn; 017import javax.swing.table.TableRowSorter; 018import jmri.InstanceManager; 019import jmri.jmrix.can.cbus.eventtable.CbusEventTableDataModel; 020 021import jmri.jmrix.can.cbus.swing.CbusCommonSwing; 022import jmri.jmrix.can.cbus.swing.CbusTableRowEventDnDHandler; 023import jmri.swing.JmriJTablePersistenceManager; 024import jmri.util.swing.StayOpenCheckBoxItem; 025import jmri.util.swing.XTableColumnModel; 026import jmri.util.table.ButtonEditor; 027import jmri.util.table.ButtonRenderer; 028 029/** 030 * Pane providing a CBUS Event table. 031 * 032 * @author Steve Young (C) 2019 033 * @see CbusEventTablePane 034 * 035 * @since 4.15.5 036 */ 037public class CbusEventTable extends JScrollPane implements TableModelListener { 038 039 private final CbusEventTablePane _mainPane; 040 private CbusTableRowEventDnDHandler eventDragHandler; 041 private TableRowSorter<CbusEventTableDataModel> sorter; 042 043 private final DateFormat DATE_FORMAT = new SimpleDateFormat("HH:mm:ss EEE d MMM yyyy"); // NOI18N 044 /** 045 * Create a new CBUS Node Event Table Pane 046 * @param mainPane main Event Table Pane 047 */ 048 public CbusEventTable( CbusEventTablePane mainPane ) { 049 super(mainPane.eventTable); 050 _mainPane = mainPane; 051 init(); 052 053 } 054 055 final void init(){ 056 057 // configure items for GUI 058 CbusCommonSwing.configureTable(_mainPane.eventTable); 059 060 setPreferredSize(new Dimension(450, 200)); 061 062 setColumnRenderers((XTableColumnModel)_mainPane.eventTable.getColumnModel()); 063 064 initColumnVisibleCheckboxes(); 065 066 _mainPane.eventModel.addTableModelListener(this); 067 068 sorter = new TableRowSorter<>(_mainPane.eventModel); 069 _mainPane.eventTable.setRowSorter(sorter); 070 071 _mainPane.eventTable.setDragEnabled(true); 072 _mainPane.eventTable.setDropMode(DropMode.ON); 073 eventDragHandler = new CbusTableRowEventDnDHandler(_mainPane.getMemo(), _mainPane.eventTable); 074 _mainPane.eventTable.setTransferHandler(eventDragHandler); 075 076 } 077 078 private static final int[] EVENT_COL_MENU = new int[]{CbusEventTableDataModel.NAME_COLUMN, 079 CbusEventTableDataModel.NODE_COLUMN, CbusEventTableDataModel.EVENT_COLUMN,CbusEventTableDataModel.NODENAME_COLUMN, 080 CbusEventTableDataModel.COMMENT_COLUMN, 081 CbusEventTableDataModel.STLR_ON_COLUMN,CbusEventTableDataModel.STLR_OFF_COLUMN}; 082 083 private static final int[] LATEST_COL_MENU = new int[]{ 084 CbusEventTableDataModel.STATE_COLUMN, CbusEventTableDataModel.CANID_COLUMN, 085 CbusEventTableDataModel.LATEST_TIMESTAMP_COLUMN, CbusEventTableDataModel.EVENT_DAT_1 , 086 CbusEventTableDataModel.EVENT_DAT_2 , CbusEventTableDataModel.EVENT_DAT_3 087 }; 088 089 private void initColumnVisibleCheckboxes(){ 090 091 JMenuItem resetSession = new JMenuItem(Bundle.getMessage("ResetSessionCount")); // NOI18N 092 resetSession.addActionListener((ActionEvent e) -> _mainPane.eventModel.ta.resetAllSessionTotals() ); 093 _mainPane.evStatMenu.add(resetSession); 094 _mainPane.evStatMenu.add( new JSeparator() ); 095 096 StayOpenCheckBoxItem[] cbArray = new StayOpenCheckBoxItem[_mainPane.eventModel.getColumnCount()]; 097 098 // initialise and set default column visibiity 099 for (int i = 0; i < _mainPane.eventModel.getColumnCount(); i++) { 100 StayOpenCheckBoxItem cbi = new StayOpenCheckBoxItem(_mainPane.eventModel.getColumnName(i)); 101 cbArray[i] = cbi; 102 cbArray[i].setToolTipText(CbusEventTableDataModel.CBUS_EV_TABLE_COL_TOOLTIPS[i]); 103 TableColumn column = ((XTableColumnModel)_mainPane.eventTable.getColumnModel()).getColumnByModelIndex(i); 104 cbi.addActionListener((ActionEvent e) -> { 105 ((XTableColumnModel)_mainPane.eventTable.getColumnModel()).setColumnVisible(column, cbi.isSelected()); 106 }); 107 final int ii = i; 108 ((XTableColumnModel)_mainPane.eventTable.getColumnModel()).setColumnVisible(((XTableColumnModel)_mainPane.eventTable.getColumnModel()).getColumnByModelIndex(i), 109 Arrays.stream(CbusEventTableDataModel.INITIAL_COLS).anyMatch(j -> j == ii) 110 ); 111 112 } 113 114 InstanceManager.getOptionalDefault(JmriJTablePersistenceManager.class).ifPresent((tpm) -> { 115 tpm.persist(_mainPane.eventTable, true); 116 }); 117 118 XTableColumnModel tcm = (XTableColumnModel)_mainPane.eventTable.getColumnModel(); 119 for (int i = 0; i < _mainPane.eventModel.getColumnCount(); i++) { 120 cbArray[i].setSelected(tcm.isColumnVisible(tcm.getColumnByModelIndex(i))); 121 122 int finali = i; 123 if ( Arrays.stream(EVENT_COL_MENU).anyMatch(j -> j == finali)){ 124 _mainPane.evColMenu.add(cbArray[i]); // event columns 125 } else if ( Arrays.stream(LATEST_COL_MENU).anyMatch(j -> j == finali)){ 126 _mainPane.evJmMenu.add(cbArray[i]); // latest columns 127 } else if ( Arrays.stream(CbusEventTableDataModel.BUTTON_COLUMNS).anyMatch(j -> j == finali)) { 128 _mainPane.buttonMenu.add(cbArray[i]); // button columns 129 } else { 130 _mainPane.evStatMenu.add(cbArray[i]); // stat columns 131 } 132 } 133 134 _mainPane.evStatMenu.add( new JSeparator(),7 ); 135 _mainPane.evColMenu.add( new JSeparator(),5 ); 136 _mainPane.evJmMenu.add( new JSeparator(),3 ); 137 138 } 139 140 141 private void setColumnRenderers(XTableColumnModel tcm){ 142 for (int i = 0; i < CbusEventTableDataModel.MAX_COLUMN; i++) { 143 final int finali = i; 144 if ( Arrays.stream(CbusEventTableDataModel.BUTTON_COLUMNS).anyMatch(j -> j == finali)){ 145 tcm.getColumn(i).setCellEditor(new ButtonEditor(new JButton())); 146 tcm.getColumn(i).setCellRenderer(new ButtonRenderer()); 147 } 148 else if ( i== CbusEventTableDataModel.STLR_ON_COLUMN || i== CbusEventTableDataModel.STLR_OFF_COLUMN) { 149 tcm.getColumn(i).setCellRenderer(new CbusEventBeanCellRenderer(_mainPane.filterText, 22)); 150 } 151 else { 152 tcm.getColumn(i).setCellRenderer(getRenderer()); 153 } 154 } 155 } 156 157 158 /** 159 * Cell Renderer for table columns, highlights any text in filter input 160 */ 161 private TableCellRenderer getRenderer() { 162 return new TableCellRenderer() { 163 JTextField f = new JTextField(); 164 165 @Override 166 public Component getTableCellRendererComponent( 167 JTable table, Object arg1, boolean isSelected, boolean hasFocus, 168 int row, int col) { 169 170 if(arg1 != null){ 171 String string = arg1.toString(); 172 f.setText(string); 173 f.setHorizontalAlignment(JTextField.CENTER); 174 175 if (col != CbusEventTableDataModel.EVENT_COLUMN) { 176 CbusCommonSwing.hideNumbersLessThan(1, string, f); 177 } 178 179 CbusCommonSwing.setCellTextHighlighter(_mainPane.filterText.getText(), string, f); 180 CbusCommonSwing.setCellFromDate(arg1,f,DATE_FORMAT); 181 182 } else { 183 f.setText(""); 184 f.getHighlighter().removeAllHighlights(); 185 } 186 187 CbusCommonSwing.setCellBackground(isSelected, f, table,row); 188 CbusCommonSwing.setCellFocus(hasFocus, f, table); 189 CbusCommonSwing.setCellFromCbusEventEnum(arg1, f); 190 191 return f; 192 } 193 }; 194 } 195 196 protected void addFilterListener( JTextField filter ){ 197 198 filter.getDocument().addDocumentListener(new DocumentListener() { 199 @Override 200 public void insertUpdate(DocumentEvent e) { 201 update(e); 202 } 203 204 @Override 205 public void removeUpdate(DocumentEvent e) { 206 update(e); 207 } 208 209 @Override 210 public void changedUpdate(DocumentEvent e) { 211 update(e); 212 } 213 214 }); 215 } 216 217 public void update(DocumentEvent e) { 218 String textForSearch = _mainPane.filterText.getText(); // better searches if not trimmed 219 if (textForSearch.length() == 0) { 220 sorter.setRowFilter(null); 221 _mainPane.clearfilterButton.setEnabled(false); 222 } else { 223 _mainPane.clearfilterButton.setEnabled(true); 224 try { 225 sorter.setRowFilter( 226 RowFilter.regexFilter("(?i)" + textForSearch)); // case insensitive 227 } catch (PatternSyntaxException pse) { 228 // log.error(" bad regex "); 229 } 230 } 231 } 232 233 @Override 234 public void tableChanged(TableModelEvent e) { 235 // indexed lookup, this is called for every table change 236 _mainPane.neweventcontainer.setNewButtonActive( _mainPane.eventModel.getEventTableRow( 237 _mainPane.neweventcontainer.getNn(), _mainPane.neweventcontainer.getEn()) < 0 ); 238 } 239 240 public void addEvent(int nn,int en){ 241 _mainPane.eventModel.provideEvent(nn, en); 242 tableChanged(null); 243 } 244 245 246 public void dispose() { 247 _mainPane.eventModel.removeTableModelListener(this); 248 eventDragHandler.dispose(); 249 } 250 251 // private static final Logger log = LoggerFactory.getLogger(CbusNodeEventTablePane.class); 252 253}