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}