001package jmri.jmrit.automat.monitor; 002 003import java.beans.PropertyChangeEvent; 004import java.beans.PropertyChangeListener; 005import java.util.ArrayList; 006 007import javax.swing.JButton; 008import javax.swing.JTable; 009import javax.swing.JTextField; 010import javax.swing.SwingUtilities; 011import javax.swing.table.AbstractTableModel; 012import javax.swing.table.TableCellEditor; 013import javax.swing.table.TableColumnModel; 014 015import org.slf4j.Logger; 016import org.slf4j.LoggerFactory; 017 018import jmri.jmrit.automat.AbstractAutomaton; 019import jmri.jmrit.automat.AutomatSummary; 020import jmri.util.table.ButtonEditor; 021import jmri.util.table.ButtonRenderer; 022 023/** 024 * Table data model for display of Automat instances. 025 * 026 * 027 * @author Bob Jacobsen Copyright (C) 2004 028 */ 029public class AutomatTableDataModel extends AbstractTableModel { 030 031 static final int NAMECOL = 0; // display name 032 static final int TURNSCOL = 1; // number of times through the loop 033 static final int KILLCOL = 2; // 034 035 static final int NUMCOLUMN = 3; 036 037 AutomatSummary summary = AutomatSummary.instance(); 038 private ArrayList<AbstractAutomaton> automats = summary.getAutomats(); 039 040 private final PropertyChangeListener listener = (PropertyChangeEvent evt) -> { 041 SwingUtilities.invokeLater(() -> { 042 // the number of automations can't not change during table update 043 automats = summary.getAutomats(); 044 switch (evt.getPropertyName()) { 045 case "Insert": 046 // fireTableRowsInserted(((Integer)e.getNewValue()).intValue(), 047 // ((Integer)e.getNewValue()).intValue()); 048 fireTableDataChanged(); 049 break; 050 case "Remove": 051 // fireTableRowsDeleted(((Integer)e.getNewValue()).intValue(), 052 // ((Integer)e.getNewValue()).intValue()); 053 fireTableDataChanged(); 054 break; 055 case "Count": 056 // it's a count indication, so update TURNS 057 int row = ((Integer) evt.getNewValue()); 058 // length might have changed... 059 if (row < getRowCount()) { 060 fireTableCellUpdated(row, TURNSCOL); 061 } 062 break; 063 default: 064 log.debug("Ignoring unexpected property {}", evt.getPropertyName()); 065 break; 066 } 067 }); 068 }; 069 070 public AutomatTableDataModel() { 071 super(); 072 // listen for new/gone/changed Automat instances 073 summary.addPropertyChangeListener(this.listener); 074 } 075 076 @Override 077 public int getColumnCount() { 078 return NUMCOLUMN; 079 } 080 081 @Override 082 public int getRowCount() { 083 return automats.size(); 084 } 085 086 @Override 087 public String getColumnName(int col) { 088 switch (col) { 089 case NAMECOL: 090 return Bundle.getMessage("ColName"); 091 case TURNSCOL: 092 return Bundle.getMessage("ColCycles"); 093 case KILLCOL: 094 return Bundle.getMessage("ColKill"); // problem if this is blank? 095 096 default: 097 return Bundle.getMessage("ColUnknown"); 098 } 099 } 100 101 /** 102 * Note that this returns String even for columns that contain buttons. 103 * {@inheritDoc} 104 */ 105 @Override 106 public Class<?> getColumnClass(int col) { 107 switch (col) { 108 case NAMECOL: 109 case KILLCOL: 110 return String.class; 111 case TURNSCOL: 112 return Integer.class; 113 default: 114 return null; 115 } 116 } 117 118 @Override 119 public boolean isCellEditable(int row, int col) { 120 switch (col) { 121 case KILLCOL: 122 return true; 123 default: 124 return false; 125 } 126 } 127 128 @Override 129 public Object getValueAt(int row, int col) { 130 AbstractAutomaton automat = automats.get(row); 131 if (automat != null) { 132 switch (col) { 133 case NAMECOL: 134 return automat.getName(); 135 case TURNSCOL: 136 return automat.getCount(); 137 case KILLCOL: // return button text here 138 return Bundle.getMessage("ButtonKill"); 139 default: 140 log.error("internal state inconsistent with table requst for {} {}", row, col); 141 return null; 142 } 143 } 144 return null; 145 } 146 147 public int getPreferredWidth(int col) { 148 switch (col) { 149 case NAMECOL: 150 return new JTextField(20).getPreferredSize().width; 151 case TURNSCOL: 152 return new JTextField(5).getPreferredSize().width; 153 case KILLCOL: 154 return new JButton(Bundle.getMessage("ButtonKill")).getPreferredSize().width; 155 default: 156 log.warn("Unexpected column in getPreferredWidth: {}", col); 157 return new JTextField(5).getPreferredSize().width; 158 } 159 } 160 161 @Override 162 public void setValueAt(Object value, int row, int col) { 163 if (col == KILLCOL) { 164 // button fired, handle 165 summary.get(row).stop(); 166 } 167 } 168 169 /** 170 * Configure a table to have our standard rows and columns. This is optional, in 171 * that other table formats can use this table model. But we put it here to help 172 * keep it consistent. 173 * 174 * @param table the table to configure 175 */ 176 public void configureTable(JTable table) { 177 // allow reordering of the columns 178 table.getTableHeader().setReorderingAllowed(true); 179 180 // have to shut off autoResizeMode to get horizontal scroll to work (JavaSwing p 181 // 541) 182 table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); 183 184 // resize columns as requested 185 for (int i = 0; i < table.getColumnCount(); i++) { 186 int width = getPreferredWidth(i); 187 table.getColumnModel().getColumn(i).setPreferredWidth(width); 188 } 189 table.sizeColumnsToFit(-1); 190 191 // have the value column hold a button 192 setColumnToHoldButton(table, KILLCOL, new JButton(Bundle.getMessage("ButtonKill"))); 193 } 194 195 /** 196 * Service method to setup a column so that it will hold a button for its values 197 * 198 * @param table the table in which to configure the column 199 * @param column the position of the configured column 200 * @param sample typical button, used for size 201 */ 202 void setColumnToHoldButton(JTable table, int column, JButton sample) { 203 TableColumnModel tcm = table.getColumnModel(); 204 // install a button renderer & editor 205 ButtonRenderer buttonRenderer = new ButtonRenderer(); 206 tcm.getColumn(column).setCellRenderer(buttonRenderer); 207 TableCellEditor buttonEditor = new ButtonEditor(new JButton()); 208 tcm.getColumn(column).setCellEditor(buttonEditor); 209 // ensure the table rows, columns have enough room for buttons 210 table.setRowHeight(sample.getPreferredSize().height); 211 table.getColumnModel().getColumn(column).setPreferredWidth(sample.getPreferredSize().width); 212 } 213 214 synchronized public void dispose() { 215 AutomatSummary.instance().removePropertyChangeListener(this.listener); 216 } 217 218 private final static Logger log = LoggerFactory.getLogger(AutomatTableDataModel.class); 219 220}