001package jmri.jmrit.operations.locations.schedules; 002 003import java.awt.Color; 004import java.awt.event.ActionEvent; 005import java.beans.PropertyChangeEvent; 006import java.beans.PropertyChangeListener; 007import java.util.Hashtable; 008import java.util.List; 009 010import javax.swing.*; 011import javax.swing.table.TableCellEditor; 012import javax.swing.table.TableColumnModel; 013 014import jmri.InstanceManager; 015import jmri.jmrit.operations.OperationsTableModel; 016import jmri.jmrit.operations.OperationsXml; 017import jmri.jmrit.operations.locations.LocationManager; 018import jmri.jmrit.operations.locations.Track; 019import jmri.jmrit.operations.setup.Control; 020import jmri.util.swing.JmriJOptionPane; 021import jmri.util.table.ButtonEditor; 022import jmri.util.table.ButtonRenderer; 023 024/** 025 * Table Model for edit of schedules used by operations 026 * 027 * @author Daniel Boudreau Copyright (C) 2009, 2011, 2013 028 */ 029public class SchedulesTableModel extends OperationsTableModel implements PropertyChangeListener { 030 031 ScheduleManager scheduleManager; // There is only one manager 032 033 // Defines the columns 034 static final int ID_COLUMN = 0; 035 static final int NAME_COLUMN = ID_COLUMN + 1; 036 static final int SCHEDULE_STATUS_COLUMN = NAME_COLUMN + 1; 037 static final int SPUR_NUMBER_COLUMN = SCHEDULE_STATUS_COLUMN + 1; 038 static final int SPUR_COLUMN = SPUR_NUMBER_COLUMN + 1; 039 static final int STATUS_COLUMN = SPUR_COLUMN + 1; 040 static final int MODE_COLUMN = STATUS_COLUMN + 1; 041 static final int EDIT_COLUMN = MODE_COLUMN + 1; 042 static final int DELETE_COLUMN = EDIT_COLUMN + 1; 043 044 private static final int HIGHEST_COLUMN = DELETE_COLUMN + 1; 045 046 public SchedulesTableModel() { 047 super(); 048 scheduleManager = InstanceManager.getDefault(ScheduleManager.class); 049 scheduleManager.addPropertyChangeListener(this); 050 updateList(); 051 } 052 053 public final int SORTBYNAME = 1; 054 public final int SORTBYID = 2; 055 056 private int _sort = SORTBYNAME; 057 058 public void setSort(int sort) { 059 _sort = sort; 060 updateList(); 061 fireTableDataChanged(); 062 } 063 064 private void updateList() { 065 // first, remove listeners from the individual objects 066 removePropertyChangeSchedules(); 067 removePropertyChangeTracks(); 068 069 if (_sort == SORTBYID) { 070 sysList = scheduleManager.getSchedulesByIdList(); 071 } else { 072 sysList = scheduleManager.getSchedulesByNameList(); 073 } 074 // and add them back in 075 for (Schedule sch : sysList) { 076 sch.addPropertyChangeListener(this); 077 } 078 addPropertyChangeTracks(); 079 } 080 081 List<Schedule> sysList = null; 082 083 public void initTable(SchedulesTableFrame frame, JTable table) { 084 super.initTable(table); 085 086 // Install the button handlers 087 TableColumnModel tcm = table.getColumnModel(); 088 ButtonRenderer buttonRenderer = new ButtonRenderer(); 089 TableCellEditor buttonEditor = new ButtonEditor(new javax.swing.JButton()); 090 tcm.getColumn(EDIT_COLUMN).setCellRenderer(buttonRenderer); 091 tcm.getColumn(EDIT_COLUMN).setCellEditor(buttonEditor); 092 tcm.getColumn(DELETE_COLUMN).setCellRenderer(buttonRenderer); 093 tcm.getColumn(DELETE_COLUMN).setCellEditor(buttonEditor); 094 095 // set column preferred widths 096 table.getColumnModel().getColumn(ID_COLUMN).setPreferredWidth(40); 097 table.getColumnModel().getColumn(NAME_COLUMN).setPreferredWidth(200); 098 table.getColumnModel().getColumn(SCHEDULE_STATUS_COLUMN).setPreferredWidth(80); 099 table.getColumnModel().getColumn(SPUR_NUMBER_COLUMN).setPreferredWidth(40); 100 table.getColumnModel().getColumn(SPUR_COLUMN).setPreferredWidth(350); 101 table.getColumnModel().getColumn(STATUS_COLUMN).setPreferredWidth(150); 102 table.getColumnModel().getColumn(MODE_COLUMN).setPreferredWidth(70); 103 table.getColumnModel().getColumn(EDIT_COLUMN).setPreferredWidth(70); 104 table.getColumnModel().getColumn(DELETE_COLUMN).setPreferredWidth(90); 105 106 frame.loadTableDetails(table); 107 } 108 109 @Override 110 public int getRowCount() { 111 return sysList.size(); 112 } 113 114 @Override 115 public int getColumnCount() { 116 return HIGHEST_COLUMN; 117 } 118 119 @Override 120 public String getColumnName(int col) { 121 switch (col) { 122 case ID_COLUMN: 123 return Bundle.getMessage("Id"); 124 case NAME_COLUMN: 125 return Bundle.getMessage("Name"); 126 case SCHEDULE_STATUS_COLUMN: 127 return Bundle.getMessage("Status"); 128 case SPUR_NUMBER_COLUMN: 129 return Bundle.getMessage("Number"); 130 case SPUR_COLUMN: 131 return Bundle.getMessage("Spurs"); 132 case STATUS_COLUMN: 133 return Bundle.getMessage("StatusSpur"); 134 case MODE_COLUMN: 135 return Bundle.getMessage("ScheduleMode"); 136 case EDIT_COLUMN: 137 return Bundle.getMessage("ButtonEdit"); 138 case DELETE_COLUMN: 139 return Bundle.getMessage("ButtonDelete"); // titles above all 140 // columns 141 default: 142 return "unknown"; // NOI18N 143 } 144 } 145 146 @Override 147 public Class<?> getColumnClass(int col) { 148 switch (col) { 149 case ID_COLUMN: 150 case NAME_COLUMN: 151 case SCHEDULE_STATUS_COLUMN: 152 case STATUS_COLUMN: 153 case MODE_COLUMN: 154 return String.class; 155 case SPUR_COLUMN: 156 return JComboBox.class; 157 case SPUR_NUMBER_COLUMN: 158 return Integer.class; 159 case EDIT_COLUMN: 160 case DELETE_COLUMN: 161 return JButton.class; 162 default: 163 return null; 164 } 165 } 166 167 @Override 168 public boolean isCellEditable(int row, int col) { 169 switch (col) { 170 case EDIT_COLUMN: 171 case DELETE_COLUMN: 172 case SPUR_COLUMN: 173 return true; 174 default: 175 return false; 176 } 177 } 178 179 @Override 180 public Object getValueAt(int row, int col) { 181 if (row >= getRowCount()) { 182 return "ERROR row " + row; // NOI18N 183 } 184 Schedule schedule = sysList.get(row); 185 if (schedule == null) { 186 return "ERROR schedule unknown " + row; // NOI18N 187 } 188 switch (col) { 189 case ID_COLUMN: 190 return schedule.getId(); 191 case NAME_COLUMN: 192 return schedule.getName(); 193 case SCHEDULE_STATUS_COLUMN: 194 return getScheduleStatus(row); 195 case SPUR_NUMBER_COLUMN: 196 return scheduleManager.getSpursByScheduleComboBox(schedule).getItemCount(); 197 case SPUR_COLUMN: { 198 return getComboBox(row, schedule); 199 } 200 case STATUS_COLUMN: 201 return getSpurStatus(row); 202 case MODE_COLUMN: 203 return getSpurMode(row); 204 case EDIT_COLUMN: 205 return Bundle.getMessage("ButtonEdit"); 206 case DELETE_COLUMN: 207 return Bundle.getMessage("ButtonDelete"); 208 default: 209 return "unknown " + col; // NOI18N 210 } 211 } 212 213 @Override 214 public void setValueAt(Object value, int row, int col) { 215 switch (col) { 216 case EDIT_COLUMN: 217 editSchedule(row); 218 break; 219 case DELETE_COLUMN: 220 deleteSchedule(row); 221 break; 222 case SPUR_COLUMN: 223 selectJComboBox(value, row); 224 break; 225 default: 226 break; 227 } 228 } 229 230 @Override 231 protected Color getForegroundColor(int row) { 232 if (!getScheduleStatus(row).equals(Bundle.getMessage("ButtonOK"))) { 233 return Color.red; 234 } 235 return super.getForegroundColor(row); 236 } 237 238 ScheduleEditFrame sef = null; 239 240 private void editSchedule(int row) { 241 log.debug("Edit schedule"); 242 if (sef != null) { 243 sef.dispose(); 244 } 245 Schedule sch = sysList.get(row); 246 LocationTrackPair ltp = getLocationTrackPair(row); 247 if (ltp == null) { 248 log.debug("Need location track pair"); 249 JmriJOptionPane.showMessageDialog(null, Bundle.getMessage("AssignSchedule", sch.getName()), 250 Bundle.getMessage("CanNotSchedule", Bundle.getMessage("ButtonEdit")), 251 JmriJOptionPane.ERROR_MESSAGE); 252 return; 253 } 254 // use invokeLater so new window appears on top 255 SwingUtilities.invokeLater(() -> { 256 sef = new ScheduleEditFrame(sch, ltp.getTrack()); 257 }); 258 } 259 260 private void deleteSchedule(int row) { 261 log.debug("Delete schedule"); 262 Schedule sch = sysList.get(row); 263 if (JmriJOptionPane.showConfirmDialog(null, Bundle.getMessage("DoYouWantToDeleteSchedule", sch.getName()), 264 Bundle.getMessage("DeleteSchedule?"), JmriJOptionPane.YES_NO_OPTION) == JmriJOptionPane.YES_OPTION) { 265 scheduleManager.deregister(sch); 266 OperationsXml.save(); 267 } 268 } 269 270 protected Hashtable<Schedule, String> comboSelect = new Hashtable<Schedule, String>(); 271 272 private void selectJComboBox(Object value, int row) { 273 Schedule schedule = sysList.get(row); 274 JComboBox<?> box = (JComboBox<?>) value; 275 if (box.getSelectedIndex() >= 0) { 276 comboSelect.put(schedule, Integer.toString(box.getSelectedIndex())); 277 } 278 fireTableRowsUpdated(row, row); 279 } 280 281 private LocationTrackPair getLocationTrackPair(int row) { 282 Schedule s = sysList.get(row); 283 JComboBox<LocationTrackPair> box = scheduleManager.getSpursByScheduleComboBox(s); 284 String index = comboSelect.get(sysList.get(row)); 285 LocationTrackPair ltp; 286 if (index != null) { 287 ltp = box.getItemAt(Integer.parseInt(index)); 288 } else { 289 ltp = box.getItemAt(0); 290 } 291 return ltp; 292 } 293 294 private String getScheduleStatus(int row) { 295 Schedule sch = sysList.get(row); 296 JComboBox<?> box = scheduleManager.getSpursByScheduleComboBox(sch); 297 for (int i = 0; i < box.getItemCount(); i++) { 298 LocationTrackPair ltp = (LocationTrackPair) box.getItemAt(i); 299 String status = ltp.getTrack().checkScheduleValid(); 300 if (!status.equals(Schedule.SCHEDULE_OKAY)) { 301 return Bundle.getMessage("ErrorTitle"); 302 } 303 } 304 return Bundle.getMessage("ButtonOK"); 305 } 306 307 private JComboBox<LocationTrackPair> getComboBox(int row, Schedule schedule) { 308 JComboBox<LocationTrackPair> box = scheduleManager.getSpursByScheduleComboBox(schedule); 309 String index = comboSelect.get(sysList.get(row)); 310 if (index != null && box.getItemCount() > Integer.parseInt(index)) { 311 box.setSelectedIndex(Integer.parseInt(index)); 312 } 313 box.addActionListener((ActionEvent e) -> { 314 comboBoxActionPerformed(e); 315 }); 316 return box; 317 } 318 319 protected void comboBoxActionPerformed(ActionEvent ae) { 320 log.debug("combobox action"); 321 if (_table.isEditing()) { 322 _table.getCellEditor().stopCellEditing(); // Allows the table 323 // contents to update 324 } 325 } 326 327 private String getSpurStatus(int row) { 328 LocationTrackPair ltp = getLocationTrackPair(row); 329 if (ltp == null) { 330 return ""; 331 } 332 String status = ltp.getTrack().checkScheduleValid(); 333 if (!status.equals(Schedule.SCHEDULE_OKAY)) { 334 return status; 335 } 336 return Bundle.getMessage("ButtonOK"); 337 } 338 339 private String getSpurMode(int row) { 340 LocationTrackPair ltp = getLocationTrackPair(row); 341 if (ltp == null) { 342 return ""; 343 } 344 return ltp.getTrack().getScheduleModeName(); 345 } 346 347 private void removePropertyChangeSchedules() { 348 if (sysList != null) { 349 for (Schedule sch : sysList) { 350 sch.removePropertyChangeListener(this); 351 } 352 } 353 } 354 355 private void addPropertyChangeTracks() { 356 // only spurs have schedules 357 for (Track track : InstanceManager.getDefault(LocationManager.class).getTracks(Track.SPUR)) { 358 track.addPropertyChangeListener(this); 359 } 360 } 361 362 private void removePropertyChangeTracks() { 363 for (Track track : InstanceManager.getDefault(LocationManager.class).getTracks(Track.SPUR)) { 364 track.removePropertyChangeListener(this); 365 } 366 } 367 368 public void dispose() { 369 if (sef != null) { 370 sef.dispose(); 371 } 372 scheduleManager.removePropertyChangeListener(this); 373 removePropertyChangeSchedules(); 374 removePropertyChangeTracks(); 375 } 376 377 // check for change in number of schedules, or a change in a schedule 378 @Override 379 public void propertyChange(PropertyChangeEvent e) { 380 if (Control.SHOW_PROPERTY) { 381 log.debug("Property change: ({}) old: ({}) new: ({})", e.getPropertyName(), e.getOldValue(), e 382 .getNewValue()); 383 } 384 if (e.getPropertyName().equals(ScheduleManager.LISTLENGTH_CHANGED_PROPERTY)) { 385 updateList(); 386 fireTableDataChanged(); 387 } else if (e.getSource().getClass().equals(Schedule.class)) { 388 Schedule schedule = (Schedule) e.getSource(); 389 int row = sysList.indexOf(schedule); 390 if (Control.SHOW_PROPERTY) { 391 log.debug("Update schedule table row: {} name: {}", row, schedule.getName()); 392 } 393 if (row >= 0) { 394 fireTableRowsUpdated(row, row); 395 } 396 } 397 if (e.getSource().getClass().equals(Track.class)) { 398 Track track = (Track) e.getSource(); 399 Schedule schedule = track.getSchedule(); 400 int row = sysList.indexOf(schedule); 401 if (row >= 0) { 402 fireTableRowsUpdated(row, row); 403 } else { 404 fireTableDataChanged(); 405 } 406 } 407 408 if (e.getPropertyName().equals(Track.SCHEDULE_ID_CHANGED_PROPERTY)) { 409 fireTableDataChanged(); 410 } 411 } 412 413 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(SchedulesTableModel.class); 414}