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