001package jmri.jmrit.beantable.turnout; 002 003import java.awt.Component; 004import java.awt.event.KeyEvent; 005import java.awt.event.MouseEvent; 006 007import java.util.EventObject; 008import java.util.Hashtable; 009 010import javax.annotation.Nonnull; 011import javax.swing.border.Border; 012import javax.swing.BorderFactory; 013import javax.swing.DefaultCellEditor; 014import javax.swing.JTable; 015import javax.swing.table.*; 016import javax.swing.UIManager; 017 018import jmri.InstanceManager; 019import jmri.NamedBean; 020import jmri.Sensor; 021import jmri.SensorManager; 022import jmri.Turnout; 023import jmri.swing.NamedBeanComboBox; 024import jmri.util.SystemType; 025import jmri.util.swing.JComboBoxUtil; 026 027import org.slf4j.Logger; 028import org.slf4j.LoggerFactory; 029 030/** 031 * JTable to display a TurnoutTableDataModel. 032 * Code originally within TurnoutTableAction. 033 * 034 * @author Bob Jacobsen Copyright (C) 2003, 2004, 2007 035 * @author Egbert Broerse Copyright (C) 2017 036 * @author Steve Young Copyright (C) 2021 037 */ 038public class TurnoutTableJTable extends JTable { 039 040 final TurnoutTableDataModel model; 041 042 final Hashtable<Turnout, TableCellRenderer> rendererMapSensor1 = new Hashtable<>(); 043 final Hashtable<Turnout, TableCellEditor> editorMapSensor1 = new Hashtable<>(); 044 045 final Hashtable<Turnout, TableCellRenderer> rendererMapSensor2 = new Hashtable<>(); 046 final Hashtable<Turnout, TableCellEditor> editorMapSensor2 = new Hashtable<>(); 047 048 public TurnoutTableJTable(TurnoutTableDataModel mdl){ 049 super(mdl); 050 model = mdl; 051 } 052 053 @Override 054 public String getToolTipText(@Nonnull MouseEvent e) { 055 java.awt.Point p = e.getPoint(); 056 int rowIndex = rowAtPoint(p); 057 int colIndex = columnAtPoint(p); 058 int realRowIndex = convertRowIndexToModel(rowIndex); 059 int realColumnIndex = convertColumnIndexToModel(colIndex); 060 return model.getCellToolTip(this, realRowIndex, realColumnIndex); 061 } 062 063 /** 064 * Disable Windows Key or Mac Meta Keys being pressed acting 065 * as a trigger for editing the focused cell. 066 * Causes unexpected behaviour, i.e. button presses. 067 * {@inheritDoc} 068 */ 069 @Override 070 public boolean editCellAt(int row, int column, EventObject e) { 071 if (e instanceof KeyEvent) { 072 if ( ((KeyEvent) e).getKeyCode() == KeyEvent.VK_WINDOWS 073 || ( (KeyEvent) e).getKeyCode() == KeyEvent.VK_META ) { 074 return false; 075 } 076 } 077 return super.editCellAt(row, column, e); 078 } 079 080 @Override 081 public TableCellRenderer getCellRenderer(int row, int column) { 082 // Convert the displayed index to the model index, rather than the displayed index 083 int modelColumn = this.convertColumnIndexToModel(column); 084 if (modelColumn == TurnoutTableDataModel.SENSOR1COL || modelColumn == TurnoutTableDataModel.SENSOR2COL) { 085 return getRenderer(row, modelColumn); 086 } else { 087 return super.getCellRenderer(row, column); 088 } 089 } 090 091 @Override 092 public TableCellEditor getCellEditor(int row, int column) { 093 //Convert the displayed index to the model index, rather than the displayed index 094 int modelColumn = this.convertColumnIndexToModel(column); 095 if (modelColumn == TurnoutTableDataModel.SENSOR1COL || modelColumn == TurnoutTableDataModel.SENSOR2COL) { 096 return getEditor(row, modelColumn); 097 } else { 098 return super.getCellEditor(row, column); 099 } 100 } 101 102 TableCellRenderer getRenderer(int row, int column) { 103 TableCellRenderer retval; 104 Turnout t = (Turnout) getModel().getValueAt(row, TurnoutTableDataModel.SYSNAMECOL); 105 java.util.Objects.requireNonNull(t, "SYSNAMECOL column content must be nonnull"); 106 switch (column) { 107 case TurnoutTableDataModel.SENSOR1COL: 108 retval = rendererMapSensor1.get(t); 109 break; 110 case TurnoutTableDataModel.SENSOR2COL: 111 retval = rendererMapSensor2.get(t); 112 break; 113 default: 114 return null; 115 } 116 117 if (retval == null) { 118 if (column == TurnoutTableDataModel.SENSOR1COL) { 119 loadRenderEditMaps(rendererMapSensor1, editorMapSensor1, t, t.getFirstSensor()); 120 } else { 121 loadRenderEditMaps(rendererMapSensor2, editorMapSensor2, t, t.getSecondSensor()); 122 } 123 retval = rendererMapSensor1.get(t); 124 } 125 log.debug("fetched for Turnout \"{}\" renderer {}", t, retval); 126 return retval; 127 } 128 129 TableCellEditor getEditor(int row, int column) { 130 TableCellEditor retval; 131 Turnout t = (Turnout) getModel().getValueAt(row, TurnoutTableDataModel.SYSNAMECOL); 132 java.util.Objects.requireNonNull(t, "SYSNAMECOL column content must be nonnull"); 133 switch (column) { 134 case TurnoutTableDataModel.SENSOR1COL: 135 retval = editorMapSensor1.get(t); 136 break; 137 case TurnoutTableDataModel.SENSOR2COL: 138 retval = editorMapSensor2.get(t); 139 break; 140 default: 141 return null; 142 } 143 if (retval == null) { 144 if (column == TurnoutTableDataModel.SENSOR1COL) { 145 loadRenderEditMaps(rendererMapSensor1, editorMapSensor1, t, t.getFirstSensor()); 146 retval = editorMapSensor1.get(t); 147 } else { //Must be two 148 loadRenderEditMaps(rendererMapSensor2, editorMapSensor2, t, t.getSecondSensor()); 149 retval = editorMapSensor2.get(t); 150 } 151 } 152 log.debug("fetched for Turnout \"{}\" editor {}", t, retval); 153 return retval; 154 } 155 156 protected void loadRenderEditMaps(Hashtable<Turnout, TableCellRenderer> r, Hashtable<Turnout, TableCellEditor> e, 157 Turnout t, Sensor s) { 158 NamedBeanComboBox<Sensor> c = new NamedBeanComboBox<>(InstanceManager.getDefault(SensorManager.class), s, NamedBean.DisplayOptions.DISPLAYNAME); 159 c.setAllowNull(true); 160 JComboBoxUtil.setupComboBoxMaxRows(c); 161 162 BeanBoxRenderer renderer = new BeanBoxRenderer(); 163 renderer.setSelectedItem(s); 164 r.put(t, renderer); 165 166 TableCellEditor editor = new BeanComboBoxEditor(c); 167 e.put(t, editor); 168 log.debug("initialize for Turnout \"{}\" Sensor \"{}\"", t, s); 169 } 170 171 static class BeanBoxRenderer extends NamedBeanComboBox<Sensor> implements TableCellRenderer { 172 173 private final Border existingBorder; 174 private final Border errorBorder = BorderFactory.createLineBorder(java.awt.Color.RED); 175 176 public BeanBoxRenderer() { 177 super(InstanceManager.getDefault(SensorManager.class)); 178 setAllowNull(true); 179 existingBorder = this.getBorder(); 180 } 181 182 @Override 183 public Component getTableCellRendererComponent(JTable table, Object value, 184 boolean isSelected, boolean hasFocus, int row, int column) { 185 if (!(SystemType.isMacOSX() && UIManager.getLookAndFeel().isNativeLookAndFeel())) { 186 if (isSelected) { 187 setForeground(table.getSelectionForeground()); 188 super.setBackground(table.getSelectionBackground()); 189 } else { 190 setForeground(table.getForeground()); 191 setBackground(table.getBackground()); 192 } 193 } 194 195 int tableCol = table.convertColumnIndexToModel(column); 196 int tableRow = table.convertRowIndexToModel(row); 197 198 Turnout t = (Turnout)table.getModel().getValueAt(tableRow, TurnoutTableDataModel.SYSNAMECOL); 199 if ( t == null ) { 200 return this; 201 } 202 if (value instanceof Sensor) { 203 setSelectedItem(value); 204 if (( tableCol == TurnoutTableDataModel.SENSOR1COL ) && 205 (t.getFeedbackMode() != Turnout.ONESENSOR && t.getFeedbackMode() != Turnout.TWOSENSOR )) { 206 setBorder(errorBorder); 207 } else if ( tableCol == TurnoutTableDataModel.SENSOR2COL && t.getFeedbackMode() != Turnout.TWOSENSOR ) { 208 setBorder(errorBorder); 209 } else { 210 setBorder(existingBorder); 211 } 212 } else { 213 setSelectedItem(null); 214 if (( tableCol == TurnoutTableDataModel.SENSOR1COL ) && 215 (t.getFeedbackMode() == Turnout.ONESENSOR || t.getFeedbackMode() == Turnout.TWOSENSOR )) { 216 setBorder(errorBorder); 217 } else if ( tableCol == TurnoutTableDataModel.SENSOR2COL && t.getFeedbackMode() == Turnout.TWOSENSOR ) { 218 setBorder(errorBorder); 219 } else { 220 setBorder(existingBorder); 221 } 222 } 223 return this; 224 } 225 } 226 227 static class BeanComboBoxEditor extends DefaultCellEditor { 228 229 public BeanComboBoxEditor(NamedBeanComboBox<Sensor> beanBox) { 230 super(beanBox); 231 } 232 } 233 234 235private final static Logger log = LoggerFactory.getLogger(TurnoutTableJTable.class); 236 237}