001package jmri.jmrit.operations.locations; 002 003import java.beans.PropertyChangeEvent; 004import java.beans.PropertyChangeListener; 005import java.util.ArrayList; 006import java.util.List; 007 008import javax.swing.*; 009import javax.swing.table.TableCellEditor; 010 011import org.slf4j.Logger; 012import org.slf4j.LoggerFactory; 013 014import jmri.InstanceManager; 015import jmri.jmrit.operations.setup.Control; 016import jmri.jmrit.operations.setup.Setup; 017import jmri.util.swing.XTableColumnModel; 018import jmri.util.table.ButtonEditor; 019import jmri.util.table.ButtonRenderer; 020 021/** 022 * Table Model for edit of locations used by operations 023 * 024 * @author Daniel Boudreau Copyright (C) 2008, 2013 025 */ 026public class LocationsTableModel extends javax.swing.table.AbstractTableModel implements PropertyChangeListener { 027 028 LocationManager locationManager; // There is only one manager 029 protected JTable _table; 030 031 // Define the columns 032 public static final int ID_COLUMN = 0; 033 public static final int NAME_COLUMN = 1; 034 public static final int TRACK_COLUMN = 2; 035 public static final int NUMBER_COLUMN = 3; 036 public static final int LENGTH_COLUMN = 4; 037 public static final int USED_LENGTH_COLUMN = 5; 038 public static final int ROLLINGSTOCK_COLUMN = 6; 039 protected static final int CARS_COLUMN = 7; 040 protected static final int LOCOS_COLUMN = 8; 041 public static final int PICKUPS_COLUMN = 9; 042 public static final int DROPS_COLUMN = 10; 043 public static final int DIVISION_COLUMN = 11; 044 public static final int REPORTER_COLUMN = 12; 045 public static final int ACTION_COLUMN = 13; 046 public static final int EDIT_COLUMN = 14; 047 048 private static final int HIGHEST_COLUMN = EDIT_COLUMN + 1; 049 050 public LocationsTableModel() { 051 super(); 052 locationManager = InstanceManager.getDefault(LocationManager.class); 053 locationManager.addPropertyChangeListener(this); 054 updateList(); 055 } 056 057 public final int SORTBYNAME = 1; 058 public final int SORTBYID = 2; 059 060 private int _sort = SORTBYNAME; 061 062 public void setSort(int sort) { 063 _sort = sort; 064 updateList(); 065 setColumnsVisible(); 066 fireTableDataChanged(); 067 } 068 069 private void updateList() { 070 // first, remove listeners from the individual objects 071 removePropertyChangeLocations(); 072 073 if (_sort == SORTBYID) { 074 locationsList = locationManager.getLocationsByIdList(); 075 } else { 076 locationsList = locationManager.getLocationsByNameList(); 077 } 078 // and add them back in 079 for (Location loc : locationsList) { 080 loc.addPropertyChangeListener(this); 081 } 082 } 083 084 List<Location> locationsList = null; 085 086 void initTable(LocationsTableFrame frame, JTable table) { 087 _table = table; 088 // Use XTableColumnModel so we can control which columns are visible 089 XTableColumnModel tcm = new XTableColumnModel(); 090 table.setColumnModel(tcm); 091 table.createDefaultColumnsFromModel(); 092 // Install the button handlers 093 ButtonRenderer buttonRenderer = new ButtonRenderer(); 094 TableCellEditor buttonEditor = new ButtonEditor(new javax.swing.JButton()); 095 tcm.getColumn(ACTION_COLUMN).setCellRenderer(buttonRenderer); 096 tcm.getColumn(ACTION_COLUMN).setCellEditor(buttonEditor); 097 tcm.getColumn(EDIT_COLUMN).setCellRenderer(buttonRenderer); 098 tcm.getColumn(EDIT_COLUMN).setCellEditor(buttonEditor); 099 100 // set column preferred widths 101 table.getColumnModel().getColumn(ID_COLUMN).setPreferredWidth(40); 102 table.getColumnModel().getColumn(NAME_COLUMN).setPreferredWidth(200); 103 table.getColumnModel().getColumn(TRACK_COLUMN).setPreferredWidth(Math.max(60, new JLabel( 104 Bundle.getMessage("Class/Interchange") + Bundle.getMessage("Spurs") + Bundle.getMessage("Yards")) 105 .getPreferredSize().width + 106 20)); 107 table.getColumnModel().getColumn(NUMBER_COLUMN).setPreferredWidth(40); 108 table.getColumnModel().getColumn(LENGTH_COLUMN).setPreferredWidth( 109 Math.max(60, new JLabel(getColumnName(LENGTH_COLUMN)).getPreferredSize().width + 10)); 110 table.getColumnModel().getColumn(USED_LENGTH_COLUMN).setPreferredWidth(60); 111 table.getColumnModel().getColumn(ROLLINGSTOCK_COLUMN).setPreferredWidth( 112 Math.max(80, new JLabel(getColumnName(ROLLINGSTOCK_COLUMN)).getPreferredSize().width + 10)); 113 table.getColumnModel().getColumn(CARS_COLUMN).setPreferredWidth(60); 114 table.getColumnModel().getColumn(LOCOS_COLUMN).setPreferredWidth(60); 115 table.getColumnModel().getColumn(PICKUPS_COLUMN).setPreferredWidth( 116 Math.max(60, new JLabel(getColumnName(PICKUPS_COLUMN)).getPreferredSize().width + 10)); 117 table.getColumnModel().getColumn(DROPS_COLUMN) 118 .setPreferredWidth(Math.max(60, new JLabel(getColumnName(DROPS_COLUMN)).getPreferredSize().width + 10)); 119 table.getColumnModel().getColumn(DIVISION_COLUMN).setPreferredWidth(160); 120 table.getColumnModel().getColumn(ACTION_COLUMN).setPreferredWidth( 121 Math.max(80, new JLabel(Bundle.getMessage("Yardmaster")).getPreferredSize().width + 40)); 122 table.getColumnModel().getColumn(EDIT_COLUMN).setPreferredWidth(80); 123 124 frame.loadTableDetails(table); 125 setColumnsVisible(); 126 } 127 128 protected void setColumnsVisible() { 129 XTableColumnModel tcm = (XTableColumnModel) _table.getColumnModel(); 130 tcm.setColumnVisible(tcm.getColumnByModelIndex(ID_COLUMN), locationManager.isShowIdEnabled()); 131 tcm.setColumnVisible(tcm.getColumnByModelIndex(DIVISION_COLUMN), locationManager.hasDivisions()); 132 tcm.setColumnVisible(tcm.getColumnByModelIndex(REPORTER_COLUMN), 133 Setup.isRfidEnabled() && locationManager.hasReporters()); 134 } 135 136 @Override 137 public int getRowCount() { 138 return locationsList.size(); 139 } 140 141 @Override 142 public int getColumnCount() { 143 return HIGHEST_COLUMN; 144 } 145 146 @Override 147 public String getColumnName(int col) { 148 switch (col) { 149 case ID_COLUMN: 150 return Bundle.getMessage("Id"); 151 case NAME_COLUMN: 152 return Bundle.getMessage("Name"); 153 case TRACK_COLUMN: 154 return Bundle.getMessage("Track"); 155 case NUMBER_COLUMN: 156 return Bundle.getMessage("Number"); 157 case LENGTH_COLUMN: 158 return Bundle.getMessage("Length"); 159 case USED_LENGTH_COLUMN: 160 return Bundle.getMessage("Used"); 161 case ROLLINGSTOCK_COLUMN: 162 return Bundle.getMessage("RollingStock"); 163 case LOCOS_COLUMN: 164 return Bundle.getMessage("Engines"); 165 case CARS_COLUMN: 166 return Bundle.getMessage("Cars"); 167 case PICKUPS_COLUMN: 168 return Bundle.getMessage("Pickups"); 169 case DROPS_COLUMN: 170 return Bundle.getMessage("Drop"); 171 case DIVISION_COLUMN: 172 return Bundle.getMessage("Division"); 173 case REPORTER_COLUMN: 174 return Bundle.getMessage("Reporters"); 175 case ACTION_COLUMN: 176 return Bundle.getMessage("Action"); 177 case EDIT_COLUMN: 178 return Bundle.getMessage("ButtonEdit"); // titles above all columns 179 default: 180 return "unknown"; // NOI18N 181 } 182 } 183 184 @Override 185 public Class<?> getColumnClass(int col) { 186 switch (col) { 187 case NAME_COLUMN: 188 case TRACK_COLUMN: 189 case DIVISION_COLUMN: 190 case REPORTER_COLUMN: 191 return String.class; 192 case ID_COLUMN: 193 case NUMBER_COLUMN: 194 case LENGTH_COLUMN: 195 case USED_LENGTH_COLUMN: 196 case ROLLINGSTOCK_COLUMN: 197 case LOCOS_COLUMN: 198 case CARS_COLUMN: 199 case PICKUPS_COLUMN: 200 case DROPS_COLUMN: 201 return Integer.class; 202 case ACTION_COLUMN: 203 case EDIT_COLUMN: 204 return JButton.class; 205 default: 206 return null; 207 } 208 } 209 210 @Override 211 public boolean isCellEditable(int row, int col) { 212 switch (col) { 213 case EDIT_COLUMN: 214 case ACTION_COLUMN: 215 return true; 216 default: 217 return false; 218 } 219 } 220 221 @Override 222 public Object getValueAt(int row, int col) { 223 if (row >= getRowCount()) { 224 return "ERROR row " + row; // NOI18N 225 } 226 Location location = locationsList.get(row); 227 if (location == null) { 228 return "ERROR location unknown " + row; // NOI18N 229 } 230 switch (col) { 231 case ID_COLUMN: 232 return Integer.parseInt(location.getId()); 233 case NAME_COLUMN: 234 return location.getName(); 235 case TRACK_COLUMN: 236 return getTrackTypes(location); 237 case NUMBER_COLUMN: 238 return location.getTracksList().size(); 239 case LENGTH_COLUMN: 240 return location.getLength(); 241 case USED_LENGTH_COLUMN: 242 return location.getUsedLength(); 243 case ROLLINGSTOCK_COLUMN: 244 return location.getNumberRS(); 245 case LOCOS_COLUMN: 246 return location.getNumberEngines(); 247 case CARS_COLUMN: 248 return location.getNumberCars(); 249 case PICKUPS_COLUMN: 250 return location.getPickupRS(); 251 case DROPS_COLUMN: 252 return location.getDropRS(); 253 case DIVISION_COLUMN: 254 return location.getDivisionName(); 255 case REPORTER_COLUMN: 256 return location.getReporterName(); 257 case ACTION_COLUMN: 258 return Bundle.getMessage("Yardmaster"); 259 case EDIT_COLUMN: 260 return Bundle.getMessage("ButtonEdit"); 261 default: 262 return "unknown " + col; // NOI18N 263 } 264 } 265 266 private String getTrackTypes(Location location) { 267 if (location.isStaging()) { 268 return (Bundle.getMessage("Staging")); 269 } else { 270 StringBuffer sb = new StringBuffer(); 271 if (location.hasInterchanges()) { 272 sb.append(Bundle.getMessage("Class/Interchange") + " "); 273 } 274 if (location.hasSpurs()) { 275 sb.append(Bundle.getMessage("Spurs") + " "); 276 } 277 if (location.hasYards()) { 278 sb.append(Bundle.getMessage("Yards")); 279 } 280 return sb.toString(); 281 } 282 } 283 284 @Override 285 public void setValueAt(Object value, int row, int col) { 286 switch (col) { 287 case ACTION_COLUMN: 288 launchYardmaster(row); 289 break; 290 case EDIT_COLUMN: 291 editLocation(row); 292 break; 293 default: 294 break; 295 } 296 } 297 298 List<LocationEditFrame> frameList = new ArrayList<LocationEditFrame>(); 299 300 private void editLocation(int row) { 301 // use invokeLater so new window appears on top 302 SwingUtilities.invokeLater(() -> { 303 Location loc = locationsList.get(row); 304 log.debug("Edit location ({})", loc.getName()); 305 for (LocationEditFrame lef : frameList) { 306 if (lef._location == loc) { 307 lef.dispose(); 308 frameList.remove(lef); 309 break; 310 } 311 } 312 LocationEditFrame lef = new LocationEditFrame(loc); 313 frameList.add(lef); 314 }); 315 } 316 317 private void launchYardmaster(int row) { 318 // use invokeLater so new window appears on top 319 SwingUtilities.invokeLater(() -> { 320 log.debug("Yardmaster"); 321 Location loc = locationsList.get(row); 322 new YardmasterFrame(loc); 323 }); 324 } 325 326 @Override 327 public void propertyChange(PropertyChangeEvent e) { 328 if (Control.SHOW_PROPERTY) { 329 log.debug("Property change: ({}) old: ({}) new: ({})", e.getPropertyName(), e.getOldValue(), 330 e.getNewValue()); 331 } 332 if (e.getPropertyName().equals(LocationManager.LISTLENGTH_CHANGED_PROPERTY)) { 333 updateList(); 334 fireTableDataChanged(); 335 } else if (e.getSource().getClass().equals(Location.class)) { 336 Location loc = (Location) e.getSource(); 337 int row = locationsList.indexOf(loc); 338 if (Control.SHOW_PROPERTY) { 339 log.debug("Update location table row: {} name: {}", row, loc.getName()); 340 } 341 if (row >= 0) { 342 fireTableRowsUpdated(row, row); 343 } 344 if (e.getPropertyName().equals(Location.LOCATION_REPORTER_CHANGED_PROPERTY) || 345 e.getPropertyName().equals(Location.LOCATION_DIVISION_CHANGED_PROPERTY)) { 346 setColumnsVisible(); 347 } 348 } 349 } 350 351 private void removePropertyChangeLocations() { 352 if (locationsList != null) { 353 for (Location loc : locationsList) { 354 loc.removePropertyChangeListener(this); 355 } 356 } 357 } 358 359 public void dispose() { 360 for (LocationEditFrame lef : frameList) { 361 lef.dispose(); 362 } 363 locationManager.removePropertyChangeListener(this); 364 removePropertyChangeLocations(); 365 } 366 367 private final static Logger log = LoggerFactory.getLogger(LocationsTableModel.class); 368}