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}