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}