001package jmri.jmrix.can.cbus.swing.eventrequestmonitor;
002
003import java.awt.BorderLayout;
004import java.awt.Color;
005import java.awt.Component;
006import java.awt.Dimension;
007import java.awt.event.ActionEvent;
008import java.text.SimpleDateFormat;
009import java.util.ArrayList;
010import java.util.Date;
011import java.util.List;
012import java.util.Objects;
013import javax.swing.*;
014import javax.swing.event.TableModelEvent;
015import javax.swing.event.TableModelListener;
016import javax.swing.table.DefaultTableCellRenderer;
017import javax.swing.table.TableColumn;
018import jmri.jmrix.can.CanSystemConnectionMemo;
019import jmri.jmrix.can.cbus.swing.CbusNewEventPane;
020import jmri.jmrix.can.cbus.swing.CbusCommonSwing;
021import jmri.util.swing.StayOpenCheckBoxItem;
022import jmri.util.swing.XTableColumnModel;
023import jmri.util.table.ButtonEditor;
024import jmri.util.table.ButtonRenderer;
025import jmri.util.table.JTableWithColumnToolTips;
026
027import org.slf4j.Logger;
028import org.slf4j.LoggerFactory;
029
030/**
031 * Frame providing a Cbus event request table.
032 *
033 * @author Steve Young (C) 2019
034 * @see CbusEventRequestDataModel
035 *
036 * @since 4.15.3
037 */
038public class CbusEventRequestTablePane extends jmri.jmrix.can.swing.CanPanel implements TableModelListener {
039
040    protected CbusEventRequestDataModel eventModel;
041    protected JTable eventTable;
042    private JScrollPane eventScroll;
043    private JSplitPane split;
044    
045    private CbusNewEventPane neweventcontainer;
046    
047    private final double _splitratio = 0.95;
048
049    private JScrollPane scrolltablefeedback;
050    public static final Color VERY_LIGHT_RED = new Color(255,176,173);
051    public static final Color VERY_LIGHT_GREEN = new Color(165,255,164);
052    public static final Color GOLD = new Color(255,204,51);
053    private final JMenu evColMenu = new JMenu(Bundle.getMessage("evColMenuName"));
054    private final JMenu evFbMenu = new JMenu(Bundle.getMessage("evFbMenuName"));
055
056    @Override
057    public void initComponents(CanSystemConnectionMemo memo) {
058        super.initComponents(memo);
059        eventModel = new CbusEventRequestDataModel(memo, 10,
060                CbusEventRequestDataModel.MAX_COLUMN); // controller, row, column
061        init();
062    }
063
064    public CbusEventRequestTablePane() {
065        super();
066    }
067
068    public void init() {
069        
070        JTable _evReqTable = new JTableWithColumnToolTips(eventModel,CbusEventRequestDataModel.columnToolTips);
071
072        // Use XTableColumnModel so we can control which columns are visible
073        final  XTableColumnModel tcm = new XTableColumnModel();
074        _evReqTable.setColumnModel(tcm);
075        
076        scrolltablefeedback = new JScrollPane (eventModel.tablefeedback());
077        
078        CbusCommonSwing.configureTable(_evReqTable);
079        
080        tcm.getColumn(CbusEventRequestDataModel.FEEDBACKREQUIRED_COLUMN).setCellRenderer(new OsRenderer());        
081        tcm.getColumn(CbusEventRequestDataModel.FEEDBACKOUTSTANDING_COLUMN).setCellRenderer(new OsRenderer());
082        tcm.getColumn(CbusEventRequestDataModel.FEEDBACKEVENT_COLUMN).setCellRenderer(new OsRenderer());        
083        tcm.getColumn(CbusEventRequestDataModel.FEEDBACKNODE_COLUMN).setCellRenderer(new OsRenderer());
084        tcm.getColumn(CbusEventRequestDataModel.FEEDBACKTIMEOUT_COLUMN).setCellRenderer(new OsRenderer());
085        tcm.getColumn(CbusEventRequestDataModel.LASTFEEDBACK_COLUMN).setCellRenderer(new LafbRenderer());
086        tcm.getColumn(CbusEventRequestDataModel.LATEST_TIMESTAMP_COLUMN).setCellRenderer(new TimeStampRenderer());
087        
088        TableColumn delBColumn = tcm.getColumn(CbusEventRequestDataModel.DELETE_BUTTON_COLUMN);
089        delBColumn.setCellEditor(new ButtonEditor(new JButton()));
090        delBColumn.setCellRenderer(new ButtonRenderer());
091        
092        TableColumn rqStatColumn = tcm.getColumn(CbusEventRequestDataModel.STATUS_REQUEST_BUTTON_COLUMN);
093        rqStatColumn.setCellEditor(new ButtonEditor(new JButton()));
094        rqStatColumn.setCellRenderer(new ButtonRenderer());
095
096        for (int i = 0; i < tcm.getColumnCount(); i++) {
097            int colnumber=i;
098            
099            String colName = _evReqTable.getColumnName(colnumber);
100            StayOpenCheckBoxItem showcol = new StayOpenCheckBoxItem(colName);
101            showcol.setToolTipText(CbusEventRequestDataModel.columnToolTips[i]);
102            showcol.setSelected(true);
103            switch (colnumber) {
104                case CbusEventRequestDataModel.NAME_COLUMN:
105                case CbusEventRequestDataModel.NODE_COLUMN:
106                case CbusEventRequestDataModel.EVENT_COLUMN:
107                case CbusEventRequestDataModel.LATEST_TIMESTAMP_COLUMN:
108                case CbusEventRequestDataModel.DELETE_BUTTON_COLUMN:
109                    evColMenu.add(showcol); // event columns
110                    break;
111                case CbusEventRequestDataModel.STATUS_REQUEST_BUTTON_COLUMN:
112                case CbusEventRequestDataModel.FEEDBACKREQUIRED_COLUMN:
113                case CbusEventRequestDataModel.LASTFEEDBACK_COLUMN:
114                case CbusEventRequestDataModel.FEEDBACKOUTSTANDING_COLUMN:
115                case CbusEventRequestDataModel.FEEDBACKTIMEOUT_COLUMN:
116                case CbusEventRequestDataModel.FEEDBACKEVENT_COLUMN:
117                case CbusEventRequestDataModel.FEEDBACKNODE_COLUMN:
118                    evFbMenu.add(showcol); // feedback columns
119                    break;
120                default:
121                    log.warn("No menuitem defined for {}",colnumber);
122                    break;
123            }
124            
125            showcol.addActionListener((ActionEvent e) -> {
126                TableColumn column  = tcm.getColumnByModelIndex(colnumber);
127                boolean visible1 = tcm.isColumnVisible(column);
128                tcm.setColumnVisible(column, !visible1);
129            });
130        }
131                
132        eventModel.addTableModelListener(this);
133        
134        setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
135       
136        // main pane
137        JPanel _pane1 = new JPanel();
138        _pane1.setLayout(new BorderLayout());
139        
140        JPanel _toppanelcontainer = new JPanel();
141        _toppanelcontainer.setLayout(new BoxLayout(_toppanelcontainer, BoxLayout.X_AXIS));
142        // scroller for main table
143        eventScroll = new JScrollPane(_evReqTable);
144        eventScroll.setPreferredSize(new Dimension(450, 200));
145
146        // add new event
147        
148        neweventcontainer = new CbusNewEventPane(this);
149        
150        _toppanelcontainer.add(neweventcontainer);
151        _pane1.add(_toppanelcontainer, BorderLayout.PAGE_START);
152        split = new JSplitPane(JSplitPane.VERTICAL_SPLIT,
153            eventScroll, scrolltablefeedback);
154        split.setResizeWeight(_splitratio);
155        split.setContinuousLayout(true);
156
157        _pane1.add(split, BorderLayout.CENTER);
158        
159        add(_pane1);
160        _pane1.setVisible(true);
161    }
162    
163    public void addEvent(int nn,int en){
164        if ( eventModel.eventRow(nn,en) < 0 ) {
165            eventModel.addEvent(nn,en,CbusEventRequestMonitorEvent.EvState.UNKNOWN,null);
166        }
167        tableChanged(null);
168    }
169    
170    /**
171     * {@inheritDoc}
172     */
173    @Override
174    public void tableChanged(TableModelEvent e) {
175        neweventcontainer.setNewButtonActive(
176            eventModel.eventRow( neweventcontainer.getNn(),neweventcontainer.getEn())<0) ;
177    }
178  
179    /**
180     * {@inheritDoc}
181     */
182    @Override
183    public String getHelpTarget() {
184        return "package.jmri.jmrix.can.cbus.swing.eventrequestmonitor.CbusEventRequestTablePane";
185    }
186    
187    /**
188     * Creates a Menu List
189     * <p>
190     * File - Print, Print Preview, Save, SaveAs csv
191     * <p>
192     * Display - show / hide Create new event pane, show/hide bottom feedback pane
193     */
194    @Override
195    public List<JMenu> getMenus() {
196        List<JMenu> menuList = new ArrayList<>();
197        JMenu displayMenu = new JMenu(Bundle.getMessage("Display"));
198        
199        JCheckBoxMenuItem shownewevent = new JCheckBoxMenuItem((Bundle.getMessage("NewEvent")));
200        // shownewevent.setMnemonic(KeyEvent.VK_C);
201        shownewevent.setSelected(true);
202        shownewevent.addActionListener((ActionEvent e) -> 
203            neweventcontainer.setVisible(shownewevent.isSelected()));
204        
205        JCheckBoxMenuItem showinfopanel = new JCheckBoxMenuItem(Bundle.getMessage("ShowInfoPanel"));
206        // shownewevent.setMnemonic(KeyEvent.VK_C);
207        showinfopanel.setSelected(true);
208        showinfopanel.addActionListener((ActionEvent e) -> {
209            scrolltablefeedback.setVisible(showinfopanel.isSelected());
210            validate();
211            repaint();
212            split.setDividerLocation(_splitratio);
213        });
214               
215        displayMenu.add(shownewevent);
216        displayMenu.add(showinfopanel);             
217        
218
219        menuList.add(evColMenu);
220        menuList.add(evFbMenu);
221        menuList.add(displayMenu);
222        return menuList;
223    }
224
225    /**
226     * Sets timestamp format + background to last heard column
227     */    
228    private static class TimeStampRenderer extends DefaultTableCellRenderer {
229        @Override
230        public Component getTableCellRendererComponent(
231            JTable table, Object value, boolean isSelected, 
232            boolean hasFocus, int row, int column) {
233            JLabel c = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
234            if (value != null && value instanceof Date) {
235                c.setText(new SimpleDateFormat("HH:mm:ss").format((Date) value));
236            }
237            return c;
238        }
239    }
240    
241    /**
242     * Sets background to spot the lesser used number columns
243     */    
244    private static class OsRenderer extends DefaultTableCellRenderer {
245        @Override
246        public Component getTableCellRendererComponent(
247            JTable table, Object value, boolean isSelected, 
248            boolean hasFocus, int row, int column) {
249            Component cellComponent = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
250
251            int val = (int) value;
252            
253            if (val>0) {
254                cellComponent.setBackground( GOLD );
255                cellComponent.setForeground( table.getForeground() );                
256            }
257            else {
258                if (isSelected) {
259                    cellComponent.setBackground( table.getSelectionBackground() );
260                    cellComponent.setForeground( table.getSelectionBackground() );                    
261                } else {
262                    cellComponent.setBackground( table.getBackground() );
263                    cellComponent.setForeground( table.getBackground() );
264                }
265            }
266            return cellComponent;
267        }
268    }    
269    
270    /**
271     * Sets background to last feedback column
272     */    
273    private static class LafbRenderer extends DefaultTableCellRenderer {
274        @Override
275        public Component getTableCellRendererComponent(
276            JTable table, Object value, boolean isSelected, 
277            boolean hasFocus, int row, int column) {
278            JLabel c = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
279            
280            if ( value instanceof CbusEventRequestMonitorEvent.FbState ) {
281            
282                if ( Objects.equals(value , CbusEventRequestMonitorEvent.FbState.LfbFinding )) {
283                    c.setText(Bundle.getMessage("LfbFinding"));
284                    c.setBackground( GOLD );
285                    c.setForeground( table.getForeground() );
286                }
287                else if ( Objects.equals(value , CbusEventRequestMonitorEvent.FbState.LfbGood )) {
288                    c.setText(Bundle.getMessage("LfbGood"));
289                    c.setBackground( VERY_LIGHT_GREEN );
290                    c.setForeground( table.getForeground() );                
291                }
292                else if ( Objects.equals(value , CbusEventRequestMonitorEvent.FbState.LfbBad )) {
293                    c.setText(Bundle.getMessage("LfbBad"));
294                    c.setBackground( VERY_LIGHT_RED );
295                    c.setForeground( table.getForeground() );                
296                }
297                
298                else {
299                    if (isSelected) {
300                        c.setBackground( table.getSelectionBackground() );
301                        c.setForeground( table.getSelectionBackground() );                    
302                    } else {
303                        c.setBackground( table.getBackground() );
304                        c.setForeground( table.getBackground() );
305                    }
306                }
307            
308            }
309            
310            return c;
311        }
312    }
313    
314    /**
315     * {@inheritDoc}
316     */
317    @Override
318    public String getTitle() {
319        return prependConnToString(Bundle.getMessage("MenuItemEvRequestMon"));
320    }
321    
322    /**
323     * {@inheritDoc}
324     */
325    @Override
326    public void dispose() {
327        eventModel.removeTableModelListener(this);
328        eventModel.dispose();
329        eventModel = null;
330        eventTable = null;
331        eventScroll = null;
332        super.dispose();
333    }
334
335    /**
336     * Nested class to create one of these using old-style defaults.
337     */
338    static public class Default extends jmri.jmrix.can.swing.CanNamedPaneAction {
339
340        public Default() {
341            super(Bundle.getMessage("MenuItemEvRequestMon"),
342                    new jmri.util.swing.sdi.JmriJFrameInterface(),
343                    CbusEventRequestTablePane.class.getName(),
344                    jmri.InstanceManager.getDefault(CanSystemConnectionMemo.class));
345        }
346    }
347
348    private final static Logger log = LoggerFactory.getLogger(CbusEventRequestTablePane.class);
349
350}